From be0475f2a583d203465d3091ff933806a5ace613 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 16 Aug 2020 20:46:39 +0200 Subject: split of set union from set service (preliminary) --- configure.ac | 2 + po/POTFILES.in | 19 + src/include/gnunet_protocols.h | 132 + src/setu/Makefile.am | 102 + src/setu/gnunet-service-setu.c | 3482 +++++++++++++++++++++++ src/setu/gnunet-service-setu.h | 393 +++ src/setu/gnunet-service-setu_protocol.h | 226 ++ src/setu/gnunet-service-setu_strata_estimator.c | 303 ++ src/setu/gnunet-service-setu_strata_estimator.h | 169 ++ src/setu/gnunet-setu-ibf-profiler.c | 308 ++ src/setu/gnunet-setu-profiler.c | 499 ++++ src/setu/ibf.c | 409 +++ src/setu/ibf.h | 255 ++ src/setu/ibf_sim.c | 142 + src/setu/plugin_block_setu_test.c | 123 + src/setu/setu.h | 304 ++ src/setu/setu_api.c | 872 ++++++ src/setu/test_setu_api.c | 360 +++ 18 files changed, 8100 insertions(+) create mode 100644 src/setu/Makefile.am create mode 100644 src/setu/gnunet-service-setu.c create mode 100644 src/setu/gnunet-service-setu.h create mode 100644 src/setu/gnunet-service-setu_protocol.h create mode 100644 src/setu/gnunet-service-setu_strata_estimator.c create mode 100644 src/setu/gnunet-service-setu_strata_estimator.h create mode 100644 src/setu/gnunet-setu-ibf-profiler.c create mode 100644 src/setu/gnunet-setu-profiler.c create mode 100644 src/setu/ibf.c create mode 100644 src/setu/ibf.h create mode 100644 src/setu/ibf_sim.c create mode 100644 src/setu/plugin_block_setu_test.c create mode 100644 src/setu/setu.h create mode 100644 src/setu/setu_api.c create mode 100644 src/setu/test_setu_api.c diff --git a/configure.ac b/configure.ac index e492242a6..bb205220c 100644 --- a/configure.ac +++ b/configure.ac @@ -1937,6 +1937,8 @@ src/scalarproduct/Makefile src/scalarproduct/scalarproduct.conf src/set/Makefile src/set/set.conf +src/setu/Makefile +src/setu/setu.conf src/sq/Makefile src/statistics/Makefile src/statistics/statistics.conf diff --git a/po/POTFILES.in b/po/POTFILES.in index 1f5cc81c3..c5503c343 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -333,6 +333,25 @@ src/set/ibf.c src/set/ibf_sim.c src/set/plugin_block_set_test.c src/set/set_api.c +src/seti/gnunet-service-set.c +src/seti/gnunet-service-set_intersection.c +src/seti/gnunet-service-set_union.c +src/seti/gnunet-service-set_union_strata_estimator.c +src/seti/gnunet-set-ibf-profiler.c +src/seti/gnunet-set-profiler.c +src/seti/ibf.c +src/seti/ibf_sim.c +src/seti/plugin_block_set_test.c +src/seti/set_api.c +src/setu/gnunet-service-set_union.c +src/setu/gnunet-service-set_union_strata_estimator.c +src/setu/gnunet-service-setu.c +src/setu/gnunet-setu-ibf-profiler.c +src/setu/gnunet-setu-profiler.c +src/setu/ibf.c +src/setu/ibf_sim.c +src/setu/plugin_block_setu_test.c +src/setu/setu_api.c src/sq/sq.c src/sq/sq_exec.c src/sq/sq_prepare.c diff --git a/src/include/gnunet_protocols.h b/src/include/gnunet_protocols.h index 5af58664f..c3fcde0b9 100644 --- a/src/include/gnunet_protocols.h +++ b/src/include/gnunet_protocols.h @@ -1658,10 +1658,142 @@ extern "C" { #define GNUNET_MESSAGE_TYPE_CONSENSUS_P2P_ROUND_CONTEXT 547 +/******************************************************************************* + * SETU message types + ******************************************************************************/ + + +/** + * Cancel a set operation + */ +#define GNUNET_MESSAGE_TYPE_SETU_CANCEL 550 + +/** + * Add element to set + */ +#define GNUNET_MESSAGE_TYPE_SETU_ADD 551 + +/** + * Create a new local set + */ +#define GNUNET_MESSAGE_TYPE_SETU_CREATE 552 + +/** + * Handle result message from operation + */ +#define GNUNET_MESSAGE_TYPE_SETU_RESULT 553 + +/** + * Evaluate a set operation + */ +#define GNUNET_MESSAGE_TYPE_SETU_EVALUATE 554 + +/** + * Listen for operation requests + */ +#define GNUNET_MESSAGE_TYPE_SETU_LISTEN 555 + +/** + * Reject a set request. + */ +#define GNUNET_MESSAGE_TYPE_SETU_REJECT 556 + +/** + * Accept an incoming set request + */ +#define GNUNET_MESSAGE_TYPE_SETU_ACCEPT 557 + +/** + * Notify the client of an incoming request from a remote peer + */ +#define GNUNET_MESSAGE_TYPE_SETU_REQUEST 558 + + +/** + * Demand the whole element from the other + * peer, given only the hash code. + */ +#define GNUNET_MESSAGE_TYPE_SETU_P2P_REQUEST_FULL 565 + +/** + * Demand the whole element from the other + * peer, given only the hash code. + */ +#define GNUNET_MESSAGE_TYPE_SETU_P2P_DEMAND 566 + +/** + * Tell the other peer to send us a list of + * hashes that match an IBF key. + */ +#define GNUNET_MESSAGE_TYPE_SETU_P2P_INQUIRY 567 + +/** + * Tell the other peer which hashes match a + * given IBF key. + */ +#define GNUNET_MESSAGE_TYPE_SETU_P2P_OFFER 568 + +/** + * Request a set union operation from a remote peer. + */ +#define GNUNET_MESSAGE_TYPE_SETU_P2P_OPERATION_REQUEST 581 + +/** + * Strata estimator. + */ +#define GNUNET_MESSAGE_TYPE_SETU_P2P_SE 582 + +/** + * Invertible bloom filter. + */ +#define GNUNET_MESSAGE_TYPE_SETU_P2P_IBF 583 + +/** + * Actual set elements. + */ +#define GNUNET_MESSAGE_TYPE_SETU_P2P_ELEMENTS 584 + +/** + * Requests for the elements with the given hashes. + */ +#define GNUNET_MESSAGE_TYPE_SETU_P2P_ELEMENT_REQUESTS 585 + +/** + * Set operation is done. + */ +#define GNUNET_MESSAGE_TYPE_SETU_P2P_DONE 586 + +/** + * Compressed strata estimator. + */ +#define GNUNET_MESSAGE_TYPE_SETU_P2P_SEC 590 + +/** + * Request all missing elements from the other peer, + * based on their sets and the elements we previously sent + * with #GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENTS. + */ +#define GNUNET_MESSAGE_TYPE_SETU_P2P_FULL_DONE 597 + +/** + * Send a set element, not as response to a demand but because + * we're sending the full set. + */ +#define GNUNET_MESSAGE_TYPE_SETU_P2P_FULL_ELEMENT 598 + +/** + * Request all missing elements from the other peer, + * based on their sets and the elements we previously sent + * with #GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENTS. + */ +#define GNUNET_MESSAGE_TYPE_SETU_P2P_OVER 599 + + /******************************************************************************* * SET message types ******************************************************************************/ + /** * Demand the whole element from the other * peer, given only the hash code. diff --git a/src/setu/Makefile.am b/src/setu/Makefile.am new file mode 100644 index 000000000..b37ceba51 --- /dev/null +++ b/src/setu/Makefile.am @@ -0,0 +1,102 @@ +# This Makefile.am is in the public domain +AM_CPPFLAGS = -I$(top_srcdir)/src/include + +pkgcfgdir= $(pkgdatadir)/config.d/ + +libexecdir= $(pkglibdir)/libexec/ + +plugindir = $(libdir)/gnunet + +pkgcfg_DATA = \ + setu.conf + +if USE_COVERAGE + AM_CFLAGS = -fprofile-arcs -ftest-coverage +endif + +if HAVE_TESTING +bin_PROGRAMS = \ + gnunet-setu-profiler + +noinst_PROGRAMS = \ + gnunet-setu-ibf-profiler +endif + +libexec_PROGRAMS = \ + gnunet-service-setu + +lib_LTLIBRARIES = \ + libgnunetsetu.la + +gnunet_setu_profiler_SOURCES = \ + gnunet-setu-profiler.c +gnunet_setu_profiler_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + libgnunetsetu.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(GN_LIBINTL) + + +gnunet_setu_ibf_profiler_SOURCES = \ + gnunet-setu-ibf-profiler.c \ + ibf.c +gnunet_setu_ibf_profiler_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) + +gnunet_service_setu_SOURCES = \ + gnunet-service-setu.c gnunet-service-setu.h \ + ibf.c ibf.h \ + gnunet-service-setu_strata_estimator.c gnunet-service-setu_strata_estimator.h \ + gnunet-service-setu_protocol.h +gnunet_service_setu_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/cadet/libgnunetcadet.la \ + $(top_builddir)/src/block/libgnunetblock.la \ + libgnunetsetu.la \ + $(GN_LIBINTL) + +libgnunetsetu_la_SOURCES = \ + setu_api.c setu.h +libgnunetsetu_la_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(LTLIBINTL) +libgnunetsetu_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) + +if HAVE_TESTING +check_PROGRAMS = \ + test_setu_api +endif + +if ENABLE_TEST_RUN +AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME; +TESTS = $(check_PROGRAMS) +endif + +test_setu_api_SOURCES = \ + test_setu_api.c +test_setu_api_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + libgnunetsetu.la + +plugin_LTLIBRARIES = \ + libgnunet_plugin_block_setu_test.la + +libgnunet_plugin_block_setu_test_la_SOURCES = \ + plugin_block_setu_test.c +libgnunet_plugin_block_setu_test_la_LIBADD = \ + $(top_builddir)/src/block/libgnunetblock.la \ + $(top_builddir)/src/block/libgnunetblockgroup.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(LTLIBINTL) +libgnunet_plugin_block_setu_test_la_LDFLAGS = \ + $(GN_PLUGIN_LDFLAGS) + + +EXTRA_DIST = \ + test_setu.conf diff --git a/src/setu/gnunet-service-setu.c b/src/setu/gnunet-service-setu.c new file mode 100644 index 000000000..88edc622f --- /dev/null +++ b/src/setu/gnunet-service-setu.c @@ -0,0 +1,3482 @@ +/* + This file is part of GNUnet + Copyright (C) 2013-2017, 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file setu/gnunet-service-setu.c + * @brief set union operation + * @author Florian Dold + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_statistics_service.h" +#include "gnunet-service-setu.h" +#include "ibf.h" +#include "gnunet-service-setu_strata_estimator.h" +#include "gnunet-service-setu_protocol.h" +#include "gnunet_statistics_service.h" +#include + + +#define LOG(kind, ...) GNUNET_log_from (kind, "set-union", __VA_ARGS__) + +/** + * How long do we hold on to an incoming channel if there is + * no local listener before giving up? + */ +#define INCOMING_CHANNEL_TIMEOUT GNUNET_TIME_UNIT_MINUTES + +/** + * Number of IBFs in a strata estimator. + */ +#define SE_STRATA_COUNT 32 + +/** + * Size of the IBFs in the strata estimator. + */ +#define SE_IBF_SIZE 80 + +/** + * The hash num parameter for the difference digests and strata estimators. + */ +#define SE_IBF_HASH_NUM 4 + +/** + * Number of buckets that can be transmitted in one message. + */ +#define MAX_BUCKETS_PER_MESSAGE ((1 << 15) / IBF_BUCKET_SIZE) + +/** + * The maximum size of an ibf we use is 2^(MAX_IBF_ORDER). + * Choose this value so that computing the IBF is still cheaper + * than transmitting all values. + */ +#define MAX_IBF_ORDER (20) + +/** + * Number of buckets used in the ibf per estimated + * difference. + */ +#define IBF_ALPHA 4 + + +/** + * Current phase we are in for a union operation. + */ +enum UnionOperationPhase +{ + /** + * We sent the request message, and expect a strata estimator. + */ + PHASE_EXPECT_SE, + + /** + * We sent the strata estimator, and expect an IBF. This phase is entered once + * upon initialization and later via #PHASE_EXPECT_ELEMENTS_AND_REQUESTS. + * + * XXX: could use better wording. + * XXX: repurposed to also expect a "request full set" message, should be renamed + * + * After receiving the complete IBF, we enter #PHASE_EXPECT_ELEMENTS + */ + PHASE_EXPECT_IBF, + + /** + * Continuation for multi part IBFs. + */ + PHASE_EXPECT_IBF_CONT, + + /** + * We are decoding an IBF. + */ + PHASE_INVENTORY_ACTIVE, + + /** + * The other peer is decoding the IBF we just sent. + */ + PHASE_INVENTORY_PASSIVE, + + /** + * The protocol is almost finished, but we still have to flush our message + * queue and/or expect some elements. + */ + PHASE_FINISH_CLOSING, + + /** + * In the penultimate phase, + * we wait until all our demands + * are satisfied. Then we send a done + * message, and wait for another done message. + */ + PHASE_FINISH_WAITING, + + /** + * In the ultimate phase, we wait until + * our demands are satisfied and then + * quit (sending another DONE message). + */ + PHASE_DONE, + + /** + * After sending the full set, wait for responses with the elements + * that the local peer is missing. + */ + PHASE_FULL_SENDING, +}; + + +/** + * State of an evaluate operation with another peer. + */ +struct OperationState +{ + /** + * Copy of the set's strata estimator at the time of + * creation of this operation. + */ + struct StrataEstimator *se; + + /** + * The IBF we currently receive. + */ + struct InvertibleBloomFilter *remote_ibf; + + /** + * The IBF with the local set's element. + */ + struct InvertibleBloomFilter *local_ibf; + + /** + * Maps unsalted IBF-Keys to elements. + * Used as a multihashmap, the keys being the lower 32bit of the IBF-Key. + * Colliding IBF-Keys are linked. + */ + struct GNUNET_CONTAINER_MultiHashMap32 *key_to_element; + + /** + * Current state of the operation. + */ + enum UnionOperationPhase phase; + + /** + * Did we send the client that we are done? + */ + int client_done_sent; + + /** + * Number of ibf buckets already received into the @a remote_ibf. + */ + unsigned int ibf_buckets_received; + + /** + * Hashes for elements that we have demanded from the other peer. + */ + struct GNUNET_CONTAINER_MultiHashMap *demanded_hashes; + + /** + * Salt that we're using for sending IBFs + */ + uint32_t salt_send; + + /** + * Salt for the IBF we've received and that we're currently decoding. + */ + uint32_t salt_receive; + + /** + * Number of elements we received from the other peer + * that were not in the local set yet. + */ + uint32_t received_fresh; + + /** + * Total number of elements received from the other peer. + */ + uint32_t received_total; + + /** + * Initial size of our set, just before + * the operation started. + */ + uint64_t initial_size; +}; + + +/** + * The key entry is used to associate an ibf key with an element. + */ +struct KeyEntry +{ + /** + * IBF key for the entry, derived from the current salt. + */ + struct IBF_Key ibf_key; + + /** + * The actual element associated with the key. + * + * Only owned by the union operation if element->operation + * is #GNUNET_YES. + */ + struct ElementEntry *element; + + /** + * Did we receive this element? + * Even if element->is_foreign is false, we might + * have received the element, so this indicates that + * the other peer has it. + */ + int received; +}; + + +/** + * Used as a closure for sending elements + * with a specific IBF key. + */ +struct SendElementClosure +{ + /** + * The IBF key whose matching elements should be + * sent. + */ + struct IBF_Key ibf_key; + + /** + * Operation for which the elements + * should be sent. + */ + struct Operation *op; +}; + + +/** + * Extra state required for efficient set union. + */ +struct SetState +{ + /** + * The strata estimator is only generated once for + * each set. + * The IBF keys are derived from the element hashes with + * salt=0. + */ + struct StrataEstimator *se; +}; + + +/** + * A listener is inhabited by a client, and waits for evaluation + * requests from remote peers. + */ +struct Listener +{ + /** + * Listeners are held in a doubly linked list. + */ + struct Listener *next; + + /** + * Listeners are held in a doubly linked list. + */ + struct Listener *prev; + + /** + * Head of DLL of operations this listener is responsible for. + * Once the client has accepted/declined the operation, the + * operation is moved to the respective set's operation DLLS. + */ + struct Operation *op_head; + + /** + * Tail of DLL of operations this listener is responsible for. + * Once the client has accepted/declined the operation, the + * operation is moved to the respective set's operation DLLS. + */ + struct Operation *op_tail; + + /** + * Client that owns the listener. + * Only one client may own a listener. + */ + struct ClientState *cs; + + /** + * The port we are listening on with CADET. + */ + struct GNUNET_CADET_Port *open_port; + + /** + * Application ID for the operation, used to distinguish + * multiple operations of the same type with the same peer. + */ + struct GNUNET_HashCode app_id; + +}; + + +/** + * Handle to the cadet service, used to listen for and connect to + * remote peers. + */ +static struct GNUNET_CADET_Handle *cadet; + +/** + * Statistics handle. + */ +struct GNUNET_STATISTICS_Handle *_GSS_statistics; + +/** + * Listeners are held in a doubly linked list. + */ +static struct Listener *listener_head; + +/** + * Listeners are held in a doubly linked list. + */ +static struct Listener *listener_tail; + +/** + * Number of active clients. + */ +static unsigned int num_clients; + +/** + * Are we in shutdown? if #GNUNET_YES and the number of clients + * drops to zero, disconnect from CADET. + */ +static int in_shutdown; + +/** + * Counter for allocating unique IDs for clients, used to identify + * incoming operation requests from remote peers, that the client can + * choose to accept or refuse. 0 must not be used (reserved for + * uninitialized). + */ +static uint32_t suggest_id; + + +/** + * Iterator over hash map entries, called to + * destroy the linked list of colliding ibf key entries. + * + * @param cls closure + * @param key current key code + * @param value value in the hash map + * @return #GNUNET_YES if we should continue to iterate, + * #GNUNET_NO if not. + */ +static int +destroy_key_to_element_iter (void *cls, + uint32_t key, + void *value) +{ + struct KeyEntry *k = value; + + GNUNET_assert (NULL != k); + if (GNUNET_YES == k->element->remote) + { + GNUNET_free (k->element); + k->element = NULL; + } + GNUNET_free (k); + return GNUNET_YES; +} + + +/** + * Destroy the union operation. Only things specific to the union + * operation are destroyed. + * + * @param op union operation to destroy + */ +static void +union_op_cancel (struct Operation *op) +{ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "destroying union op\n"); + /* check if the op was canceled twice */ + GNUNET_assert (NULL != op->state); + if (NULL != op->state->remote_ibf) + { + ibf_destroy (op->state->remote_ibf); + op->state->remote_ibf = NULL; + } + if (NULL != op->state->demanded_hashes) + { + GNUNET_CONTAINER_multihashmap_destroy (op->state->demanded_hashes); + op->state->demanded_hashes = NULL; + } + if (NULL != op->state->local_ibf) + { + ibf_destroy (op->state->local_ibf); + op->state->local_ibf = NULL; + } + if (NULL != op->state->se) + { + strata_estimator_destroy (op->state->se); + op->state->se = NULL; + } + if (NULL != op->state->key_to_element) + { + GNUNET_CONTAINER_multihashmap32_iterate (op->state->key_to_element, + &destroy_key_to_element_iter, + NULL); + GNUNET_CONTAINER_multihashmap32_destroy (op->state->key_to_element); + op->state->key_to_element = NULL; + } + GNUNET_free (op->state); + op->state = NULL; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "destroying union op done\n"); +} + + +/** + * Inform the client that the union operation has failed, + * and proceed to destroy the evaluate operation. + * + * @param op the union operation to fail + */ +static void +fail_union_operation (struct Operation *op) +{ + struct GNUNET_MQ_Envelope *ev; + struct GNUNET_SETU_ResultMessage *msg; + + LOG (GNUNET_ERROR_TYPE_WARNING, + "union operation failed\n"); + ev = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SET_RESULT); + msg->result_status = htons (GNUNET_SETU_STATUS_FAILURE); + msg->request_id = htonl (op->client_request_id); + msg->element_type = htons (0); + GNUNET_MQ_send (op->set->cs->mq, + ev); + _GSS_operation_destroy (op); +} + + +/** + * Derive the IBF key from a hash code and + * a salt. + * + * @param src the hash code + * @return the derived IBF key + */ +static struct IBF_Key +get_ibf_key (const struct GNUNET_HashCode *src) +{ + struct IBF_Key key; + uint16_t salt = 0; + + GNUNET_assert (GNUNET_OK == + GNUNET_CRYPTO_kdf (&key, sizeof(key), + src, sizeof *src, + &salt, sizeof(salt), + NULL, 0)); + return key; +} + + +/** + * Context for #op_get_element_iterator + */ +struct GetElementContext +{ + /** + * FIXME. + */ + struct GNUNET_HashCode hash; + + /** + * FIXME. + */ + struct KeyEntry *k; +}; + + +/** + * Iterator over the mapping from IBF keys to element entries. Checks if we + * have an element with a given GNUNET_HashCode. + * + * @param cls closure + * @param key current key code + * @param value value in the hash map + * @return #GNUNET_YES if we should search further, + * #GNUNET_NO if we've found the element. + */ +static int +op_get_element_iterator (void *cls, + uint32_t key, + void *value) +{ + struct GetElementContext *ctx = cls; + struct KeyEntry *k = value; + + GNUNET_assert (NULL != k); + if (0 == GNUNET_CRYPTO_hash_cmp (&k->element->element_hash, + &ctx->hash)) + { + ctx->k = k; + return GNUNET_NO; + } + return GNUNET_YES; +} + + +/** + * Determine whether the given element is already in the operation's element + * set. + * + * @param op operation that should be tested for 'element_hash' + * @param element_hash hash of the element to look for + * @return #GNUNET_YES if the element has been found, #GNUNET_NO otherwise + */ +static struct KeyEntry * +op_get_element (struct Operation *op, + const struct GNUNET_HashCode *element_hash) +{ + int ret; + struct IBF_Key ibf_key; + struct GetElementContext ctx = { { { 0 } }, 0 }; + + ctx.hash = *element_hash; + + ibf_key = get_ibf_key (element_hash); + ret = GNUNET_CONTAINER_multihashmap32_get_multiple (op->state->key_to_element, + (uint32_t) ibf_key.key_val, + op_get_element_iterator, + &ctx); + + /* was the iteration aborted because we found the element? */ + if (GNUNET_SYSERR == ret) + { + GNUNET_assert (NULL != ctx.k); + return ctx.k; + } + return NULL; +} + + +/** + * Insert an element into the union operation's + * key-to-element mapping. Takes ownership of 'ee'. + * Note that this does not insert the element in the set, + * only in the operation's key-element mapping. + * This is done to speed up re-tried operations, if some elements + * were transmitted, and then the IBF fails to decode. + * + * XXX: clarify ownership, doesn't sound right. + * + * @param op the union operation + * @param ee the element entry + * @parem received was this element received from the remote peer? + */ +static void +op_register_element (struct Operation *op, + struct ElementEntry *ee, + int received) +{ + struct IBF_Key ibf_key; + struct KeyEntry *k; + + ibf_key = get_ibf_key (&ee->element_hash); + k = GNUNET_new (struct KeyEntry); + k->element = ee; + k->ibf_key = ibf_key; + k->received = received; + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multihashmap32_put (op->state->key_to_element, + (uint32_t) ibf_key.key_val, + k, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)); +} + + +/** + * FIXME. + */ +static void +salt_key (const struct IBF_Key *k_in, + uint32_t salt, + struct IBF_Key *k_out) +{ + int s = salt % 64; + uint64_t x = k_in->key_val; + + /* rotate ibf key */ + x = (x >> s) | (x << (64 - s)); + k_out->key_val = x; +} + + +/** + * FIXME. + */ +static void +unsalt_key (const struct IBF_Key *k_in, + uint32_t salt, + struct IBF_Key *k_out) +{ + int s = salt % 64; + uint64_t x = k_in->key_val; + + x = (x << s) | (x >> (64 - s)); + k_out->key_val = x; +} + + +/** + * Insert a key into an ibf. + * + * @param cls the ibf + * @param key unused + * @param value the key entry to get the key from + */ +static int +prepare_ibf_iterator (void *cls, + uint32_t key, + void *value) +{ + struct Operation *op = cls; + struct KeyEntry *ke = value; + struct IBF_Key salted_key; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "[OP %x] inserting %lx (hash %s) into ibf\n", + (void *) op, + (unsigned long) ke->ibf_key.key_val, + GNUNET_h2s (&ke->element->element_hash)); + salt_key (&ke->ibf_key, + op->state->salt_send, + &salted_key); + ibf_insert (op->state->local_ibf, salted_key); + return GNUNET_YES; +} + + +/** + * Iterator for initializing the + * key-to-element mapping of a union operation + * + * @param cls the union operation `struct Operation *` + * @param key unused + * @param value the `struct ElementEntry *` to insert + * into the key-to-element mapping + * @return #GNUNET_YES (to continue iterating) + */ +static int +init_key_to_element_iterator (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct Operation *op = cls; + struct ElementEntry *ee = value; + + /* make sure that the element belongs to the set at the time + * of creating the operation */ + if (GNUNET_NO == + _GSS_is_element_of_operation (ee, + op)) + return GNUNET_YES; + GNUNET_assert (GNUNET_NO == ee->remote); + op_register_element (op, + ee, + GNUNET_NO); + return GNUNET_YES; +} + + +/** + * Initialize the IBF key to element mapping local to this set + * operation. + * + * @param op the set union operation + */ +static void +initialize_key_to_element (struct Operation *op) +{ + unsigned int len; + + GNUNET_assert (NULL == op->state->key_to_element); + len = GNUNET_CONTAINER_multihashmap_size (op->set->content->elements); + op->state->key_to_element = GNUNET_CONTAINER_multihashmap32_create (len + 1); + GNUNET_CONTAINER_multihashmap_iterate (op->set->content->elements, + &init_key_to_element_iterator, + op); +} + + +/** + * Create an ibf with the operation's elements + * of the specified size + * + * @param op the union operation + * @param size size of the ibf to create + * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure + */ +static int +prepare_ibf (struct Operation *op, + uint32_t size) +{ + GNUNET_assert (NULL != op->state->key_to_element); + + if (NULL != op->state->local_ibf) + ibf_destroy (op->state->local_ibf); + op->state->local_ibf = ibf_create (size, SE_IBF_HASH_NUM); + if (NULL == op->state->local_ibf) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to allocate local IBF\n"); + return GNUNET_SYSERR; + } + GNUNET_CONTAINER_multihashmap32_iterate (op->state->key_to_element, + &prepare_ibf_iterator, + op); + return GNUNET_OK; +} + + +/** + * Send an ibf of appropriate size. + * + * Fragments the IBF into multiple messages if necessary. + * + * @param op the union operation + * @param ibf_order order of the ibf to send, size=2^order + * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure + */ +static int +send_ibf (struct Operation *op, + uint16_t ibf_order) +{ + unsigned int buckets_sent = 0; + struct InvertibleBloomFilter *ibf; + + if (GNUNET_OK != + prepare_ibf (op, 1 << ibf_order)) + { + /* allocation failed */ + return GNUNET_SYSERR; + } + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "sending ibf of size %u\n", + 1 << ibf_order); + + { + char name[64] = { 0 }; + snprintf (name, sizeof(name), "# sent IBF (order %u)", ibf_order); + GNUNET_STATISTICS_update (_GSS_statistics, name, 1, GNUNET_NO); + } + + ibf = op->state->local_ibf; + + while (buckets_sent < (1 << ibf_order)) + { + unsigned int buckets_in_message; + struct GNUNET_MQ_Envelope *ev; + struct IBFMessage *msg; + + buckets_in_message = (1 << ibf_order) - buckets_sent; + /* limit to maximum */ + if (buckets_in_message > MAX_BUCKETS_PER_MESSAGE) + buckets_in_message = MAX_BUCKETS_PER_MESSAGE; + + ev = GNUNET_MQ_msg_extra (msg, + buckets_in_message * IBF_BUCKET_SIZE, + GNUNET_MESSAGE_TYPE_SET_UNION_P2P_IBF); + msg->reserved1 = 0; + msg->reserved2 = 0; + msg->order = ibf_order; + msg->offset = htonl (buckets_sent); + msg->salt = htonl (op->state->salt_send); + ibf_write_slice (ibf, buckets_sent, + buckets_in_message, &msg[1]); + buckets_sent += buckets_in_message; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "ibf chunk size %u, %u/%u sent\n", + buckets_in_message, + buckets_sent, + 1 << ibf_order); + GNUNET_MQ_send (op->mq, ev); + } + + /* The other peer must decode the IBF, so + * we're passive. */ + op->state->phase = PHASE_INVENTORY_PASSIVE; + return GNUNET_OK; +} + + +/** + * Compute the necessary order of an ibf + * from the size of the symmetric set difference. + * + * @param diff the difference + * @return the required size of the ibf + */ +static unsigned int +get_order_from_difference (unsigned int diff) +{ + unsigned int ibf_order; + + ibf_order = 2; + while (((1 << ibf_order) < (IBF_ALPHA * diff) || + ((1 << ibf_order) < SE_IBF_HASH_NUM)) && + (ibf_order < MAX_IBF_ORDER)) + ibf_order++; + // add one for correction + return ibf_order + 1; +} + + +/** + * Send a set element. + * + * @param cls the union operation `struct Operation *` + * @param key unused + * @param value the `struct ElementEntry *` to insert + * into the key-to-element mapping + * @return #GNUNET_YES (to continue iterating) + */ +static int +send_full_element_iterator (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct Operation *op = cls; + struct GNUNET_SETU_ElementMessage *emsg; + struct ElementEntry *ee = value; + struct GNUNET_SETU_Element *el = &ee->element; + struct GNUNET_MQ_Envelope *ev; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Sending element %s\n", + GNUNET_h2s (key)); + ev = GNUNET_MQ_msg_extra (emsg, + el->size, + GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_ELEMENT); + emsg->element_type = htons (el->element_type); + GNUNET_memcpy (&emsg[1], + el->data, + el->size); + GNUNET_MQ_send (op->mq, + ev); + return GNUNET_YES; +} + + +/** + * Switch to full set transmission for @a op. + * + * @param op operation to switch to full set transmission. + */ +static void +send_full_set (struct Operation *op) +{ + struct GNUNET_MQ_Envelope *ev; + + op->state->phase = PHASE_FULL_SENDING; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Dedicing to transmit the full set\n"); + /* FIXME: use a more memory-friendly way of doing this with an + iterator, just as we do in the non-full case! */ + (void) GNUNET_CONTAINER_multihashmap_iterate (op->set->content->elements, + &send_full_element_iterator, + op); + ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_DONE); + GNUNET_MQ_send (op->mq, + ev); +} + + +/** + * Handle a strata estimator from a remote peer + * + * @param cls the union operation + * @param msg the message + */ +static int +check_union_p2p_strata_estimator (void *cls, + const struct StrataEstimatorMessage *msg) +{ + struct Operation *op = cls; + int is_compressed; + size_t len; + + if (op->state->phase != PHASE_EXPECT_SE) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + is_compressed = (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SEC == htons ( + msg->header.type)); + len = ntohs (msg->header.size) - sizeof(struct StrataEstimatorMessage); + if ((GNUNET_NO == is_compressed) && + (len != SE_STRATA_COUNT * SE_IBF_SIZE * IBF_BUCKET_SIZE)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Handle a strata estimator from a remote peer + * + * @param cls the union operation + * @param msg the message + */ +static void +handle_union_p2p_strata_estimator (void *cls, + const struct StrataEstimatorMessage *msg) +{ + struct Operation *op = cls; + struct StrataEstimator *remote_se; + unsigned int diff; + uint64_t other_size; + size_t len; + int is_compressed; + + is_compressed = (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SEC == htons ( + msg->header.type)); + GNUNET_STATISTICS_update (_GSS_statistics, + "# bytes of SE received", + ntohs (msg->header.size), + GNUNET_NO); + len = ntohs (msg->header.size) - sizeof(struct StrataEstimatorMessage); + other_size = GNUNET_ntohll (msg->set_size); + remote_se = strata_estimator_create (SE_STRATA_COUNT, + SE_IBF_SIZE, + SE_IBF_HASH_NUM); + if (NULL == remote_se) + { + /* insufficient resources, fail */ + fail_union_operation (op); + return; + } + if (GNUNET_OK != + strata_estimator_read (&msg[1], + len, + is_compressed, + remote_se)) + { + /* decompression failed */ + strata_estimator_destroy (remote_se); + fail_union_operation (op); + return; + } + GNUNET_assert (NULL != op->state->se); + diff = strata_estimator_difference (remote_se, + op->state->se); + + if (diff > 200) + diff = diff * 3 / 2; + + strata_estimator_destroy (remote_se); + strata_estimator_destroy (op->state->se); + op->state->se = NULL; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "got se diff=%d, using ibf size %d\n", + diff, + 1U << get_order_from_difference (diff)); + + { + char *set_debug; + + set_debug = getenv ("GNUNET_SETU_BENCHMARK"); + if ((NULL != set_debug) && + (0 == strcmp (set_debug, "1"))) + { + FILE *f = fopen ("set.log", "a"); + fprintf (f, "%llu\n", (unsigned long long) diff); + fclose (f); + } + } + + if ((GNUNET_YES == op->byzantine) && + (other_size < op->byzantine_lower_bound)) + { + GNUNET_break (0); + fail_union_operation (op); + return; + } + + if ((GNUNET_YES == op->force_full) || + (diff > op->state->initial_size / 4) || + (0 == other_size)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Deciding to go for full set transmission (diff=%d, own set=%u)\n", + diff, + op->state->initial_size); + GNUNET_STATISTICS_update (_GSS_statistics, + "# of full sends", + 1, + GNUNET_NO); + if ((op->state->initial_size <= other_size) || + (0 == other_size)) + { + send_full_set (op); + } + else + { + struct GNUNET_MQ_Envelope *ev; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Telling other peer that we expect its full set\n"); + op->state->phase = PHASE_EXPECT_IBF; + ev = GNUNET_MQ_msg_header ( + GNUNET_MESSAGE_TYPE_SET_UNION_P2P_REQUEST_FULL); + GNUNET_MQ_send (op->mq, + ev); + } + } + else + { + GNUNET_STATISTICS_update (_GSS_statistics, + "# of ibf sends", + 1, + GNUNET_NO); + if (GNUNET_OK != + send_ibf (op, + get_order_from_difference (diff))) + { + /* Internal error, best we can do is shut the connection */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to send IBF, closing connection\n"); + fail_union_operation (op); + return; + } + } + GNUNET_CADET_receive_done (op->channel); +} + + +/** + * Iterator to send elements to a remote peer + * + * @param cls closure with the element key and the union operation + * @param key ignored + * @param value the key entry + */ +static int +send_offers_iterator (void *cls, + uint32_t key, + void *value) +{ + struct SendElementClosure *sec = cls; + struct Operation *op = sec->op; + struct KeyEntry *ke = value; + struct GNUNET_MQ_Envelope *ev; + struct GNUNET_MessageHeader *mh; + + /* Detect 32-bit key collision for the 64-bit IBF keys. */ + if (ke->ibf_key.key_val != sec->ibf_key.key_val) + return GNUNET_YES; + + ev = GNUNET_MQ_msg_header_extra (mh, + sizeof(struct GNUNET_HashCode), + GNUNET_MESSAGE_TYPE_SET_UNION_P2P_OFFER); + + GNUNET_assert (NULL != ev); + *(struct GNUNET_HashCode *) &mh[1] = ke->element->element_hash; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "[OP %x] sending element offer (%s) to peer\n", + (void *) op, + GNUNET_h2s (&ke->element->element_hash)); + GNUNET_MQ_send (op->mq, ev); + return GNUNET_YES; +} + + +/** + * Send offers (in the form of GNUNET_Hash-es) to the remote peer for the given IBF key. + * + * @param op union operation + * @param ibf_key IBF key of interest + */ +static void +send_offers_for_key (struct Operation *op, + struct IBF_Key ibf_key) +{ + struct SendElementClosure send_cls; + + send_cls.ibf_key = ibf_key; + send_cls.op = op; + (void) GNUNET_CONTAINER_multihashmap32_get_multiple ( + op->state->key_to_element, + (uint32_t) ibf_key. + key_val, + &send_offers_iterator, + &send_cls); +} + + +/** + * Decode which elements are missing on each side, and + * send the appropriate offers and inquiries. + * + * @param op union operation + * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure + */ +static int +decode_and_send (struct Operation *op) +{ + struct IBF_Key key; + struct IBF_Key last_key; + int side; + unsigned int num_decoded; + struct InvertibleBloomFilter *diff_ibf; + + GNUNET_assert (PHASE_INVENTORY_ACTIVE == op->state->phase); + + if (GNUNET_OK != + prepare_ibf (op, + op->state->remote_ibf->size)) + { + GNUNET_break (0); + /* allocation failed */ + return GNUNET_SYSERR; + } + diff_ibf = ibf_dup (op->state->local_ibf); + ibf_subtract (diff_ibf, + op->state->remote_ibf); + + ibf_destroy (op->state->remote_ibf); + op->state->remote_ibf = NULL; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "decoding IBF (size=%u)\n", + diff_ibf->size); + + num_decoded = 0; + key.key_val = 0; /* just to avoid compiler thinking we use undef'ed variable */ + + while (1) + { + int res; + int cycle_detected = GNUNET_NO; + + last_key = key; + + res = ibf_decode (diff_ibf, &side, &key); + if (res == GNUNET_OK) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "decoded ibf key %lx\n", + (unsigned long) key.key_val); + num_decoded += 1; + if ((num_decoded > diff_ibf->size) || + ((num_decoded > 1) && + (last_key.key_val == key.key_val))) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "detected cyclic ibf (decoded %u/%u)\n", + num_decoded, + diff_ibf->size); + cycle_detected = GNUNET_YES; + } + } + if ((GNUNET_SYSERR == res) || + (GNUNET_YES == cycle_detected)) + { + int next_order; + next_order = 0; + while (1 << next_order < diff_ibf->size) + next_order++; + next_order++; + if (next_order <= MAX_IBF_ORDER) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "decoding failed, sending larger ibf (size %u)\n", + 1 << next_order); + GNUNET_STATISTICS_update (_GSS_statistics, + "# of IBF retries", + 1, + GNUNET_NO); + op->state->salt_send++; + if (GNUNET_OK != + send_ibf (op, next_order)) + { + /* Internal error, best we can do is shut the connection */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to send IBF, closing connection\n"); + fail_union_operation (op); + ibf_destroy (diff_ibf); + return GNUNET_SYSERR; + } + } + else + { + GNUNET_STATISTICS_update (_GSS_statistics, + "# of failed union operations (too large)", + 1, + GNUNET_NO); + // XXX: Send the whole set, element-by-element + LOG (GNUNET_ERROR_TYPE_ERROR, + "set union failed: reached ibf limit\n"); + fail_union_operation (op); + ibf_destroy (diff_ibf); + return GNUNET_SYSERR; + } + break; + } + if (GNUNET_NO == res) + { + struct GNUNET_MQ_Envelope *ev; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "transmitted all values, sending DONE\n"); + ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DONE); + GNUNET_MQ_send (op->mq, ev); + /* We now wait until we get a DONE message back + * and then wait for our MQ to be flushed and all our + * demands be delivered. */ + break; + } + if (1 == side) + { + struct IBF_Key unsalted_key; + + unsalt_key (&key, + op->state->salt_receive, + &unsalted_key); + send_offers_for_key (op, + unsalted_key); + } + else if (-1 == side) + { + struct GNUNET_MQ_Envelope *ev; + struct InquiryMessage *msg; + + /* It may be nice to merge multiple requests, but with CADET's corking it is not worth + * the effort additional complexity. */ + ev = GNUNET_MQ_msg_extra (msg, + sizeof(struct IBF_Key), + GNUNET_MESSAGE_TYPE_SET_UNION_P2P_INQUIRY); + msg->salt = htonl (op->state->salt_receive); + GNUNET_memcpy (&msg[1], + &key, + sizeof(struct IBF_Key)); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "sending element inquiry for IBF key %lx\n", + (unsigned long) key.key_val); + GNUNET_MQ_send (op->mq, ev); + } + else + { + GNUNET_assert (0); + } + } + ibf_destroy (diff_ibf); + return GNUNET_OK; +} + + +/** + * Check an IBF message from a remote peer. + * + * Reassemble the IBF from multiple pieces, and + * process the whole IBF once possible. + * + * @param cls the union operation + * @param msg the header of the message + * @return #GNUNET_OK if @a msg is well-formed + */ +static int +check_union_p2p_ibf (void *cls, + const struct IBFMessage *msg) +{ + struct Operation *op = cls; + unsigned int buckets_in_message; + + buckets_in_message = (ntohs (msg->header.size) - sizeof *msg) + / IBF_BUCKET_SIZE; + if (0 == buckets_in_message) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if ((ntohs (msg->header.size) - sizeof *msg) != buckets_in_message + * IBF_BUCKET_SIZE) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (op->state->phase == PHASE_EXPECT_IBF_CONT) + { + if (ntohl (msg->offset) != op->state->ibf_buckets_received) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (1 << msg->order != op->state->remote_ibf->size) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (ntohl (msg->salt) != op->state->salt_receive) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + else if ((op->state->phase != PHASE_INVENTORY_PASSIVE) && + (op->state->phase != PHASE_EXPECT_IBF)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + return GNUNET_OK; +} + + +/** + * Handle an IBF message from a remote peer. + * + * Reassemble the IBF from multiple pieces, and + * process the whole IBF once possible. + * + * @param cls the union operation + * @param msg the header of the message + */ +static void +handle_union_p2p_ibf (void *cls, + const struct IBFMessage *msg) +{ + struct Operation *op = cls; + unsigned int buckets_in_message; + + buckets_in_message = (ntohs (msg->header.size) - sizeof *msg) + / IBF_BUCKET_SIZE; + if ((op->state->phase == PHASE_INVENTORY_PASSIVE) || + (op->state->phase == PHASE_EXPECT_IBF)) + { + op->state->phase = PHASE_EXPECT_IBF_CONT; + GNUNET_assert (NULL == op->state->remote_ibf); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Creating new ibf of size %u\n", + 1 << msg->order); + op->state->remote_ibf = ibf_create (1 << msg->order, SE_IBF_HASH_NUM); + op->state->salt_receive = ntohl (msg->salt); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Receiving new IBF with salt %u\n", + op->state->salt_receive); + if (NULL == op->state->remote_ibf) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse remote IBF, closing connection\n"); + fail_union_operation (op); + return; + } + op->state->ibf_buckets_received = 0; + if (0 != ntohl (msg->offset)) + { + GNUNET_break_op (0); + fail_union_operation (op); + return; + } + } + else + { + GNUNET_assert (op->state->phase == PHASE_EXPECT_IBF_CONT); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received more of IBF\n"); + } + GNUNET_assert (NULL != op->state->remote_ibf); + + ibf_read_slice (&msg[1], + op->state->ibf_buckets_received, + buckets_in_message, + op->state->remote_ibf); + op->state->ibf_buckets_received += buckets_in_message; + + if (op->state->ibf_buckets_received == op->state->remote_ibf->size) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "received full ibf\n"); + op->state->phase = PHASE_INVENTORY_ACTIVE; + if (GNUNET_OK != + decode_and_send (op)) + { + /* Internal error, best we can do is shut down */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to decode IBF, closing connection\n"); + fail_union_operation (op); + return; + } + } + GNUNET_CADET_receive_done (op->channel); +} + + +/** + * Send a result message to the client indicating + * that there is a new element. + * + * @param op union operation + * @param element element to send + * @param status status to send with the new element + */ +static void +send_client_element (struct Operation *op, + const struct GNUNET_SETU_Element *element, + int status) +{ + struct GNUNET_MQ_Envelope *ev; + struct GNUNET_SETU_ResultMessage *rm; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "sending element (size %u) to client\n", + element->size); + GNUNET_assert (0 != op->client_request_id); + ev = GNUNET_MQ_msg_extra (rm, + element->size, + GNUNET_MESSAGE_TYPE_SET_RESULT); + if (NULL == ev) + { + GNUNET_MQ_discard (ev); + GNUNET_break (0); + return; + } + rm->result_status = htons (status); + rm->request_id = htonl (op->client_request_id); + rm->element_type = htons (element->element_type); + rm->current_size = GNUNET_htonll (GNUNET_CONTAINER_multihashmap32_size ( + op->state->key_to_element)); + GNUNET_memcpy (&rm[1], + element->data, + element->size); + GNUNET_MQ_send (op->set->cs->mq, + ev); +} + + +/** + * Signal to the client that the operation has finished and + * destroy the operation. + * + * @param cls operation to destroy + */ +static void +send_client_done (void *cls) +{ + struct Operation *op = cls; + struct GNUNET_MQ_Envelope *ev; + struct GNUNET_SETU_ResultMessage *rm; + + if (GNUNET_YES == op->state->client_done_sent) + { + return; + } + + if (PHASE_DONE != op->state->phase) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + "Union operation failed\n"); + GNUNET_STATISTICS_update (_GSS_statistics, + "# Union operations failed", + 1, + GNUNET_NO); + ev = GNUNET_MQ_msg (rm, GNUNET_MESSAGE_TYPE_SET_RESULT); + rm->result_status = htons (GNUNET_SETU_STATUS_FAILURE); + rm->request_id = htonl (op->client_request_id); + rm->element_type = htons (0); + GNUNET_MQ_send (op->set->cs->mq, + ev); + return; + } + + op->state->client_done_sent = GNUNET_YES; + + GNUNET_STATISTICS_update (_GSS_statistics, + "# Union operations succeeded", + 1, + GNUNET_NO); + LOG (GNUNET_ERROR_TYPE_INFO, + "Signalling client that union operation is done\n"); + ev = GNUNET_MQ_msg (rm, + GNUNET_MESSAGE_TYPE_SET_RESULT); + rm->request_id = htonl (op->client_request_id); + rm->result_status = htons (GNUNET_SETU_STATUS_DONE); + rm->element_type = htons (0); + rm->current_size = GNUNET_htonll (GNUNET_CONTAINER_multihashmap32_size ( + op->state->key_to_element)); + GNUNET_MQ_send (op->set->cs->mq, + ev); +} + + +/** + * Tests if the operation is finished, and if so notify. + * + * @param op operation to check + */ +static void +maybe_finish (struct Operation *op) +{ + unsigned int num_demanded; + + num_demanded = GNUNET_CONTAINER_multihashmap_size ( + op->state->demanded_hashes); + + if (PHASE_FINISH_WAITING == op->state->phase) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "In PHASE_FINISH_WAITING, pending %u demands\n", + num_demanded); + if (0 == num_demanded) + { + struct GNUNET_MQ_Envelope *ev; + + op->state->phase = PHASE_DONE; + ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DONE); + GNUNET_MQ_send (op->mq, + ev); + /* We now wait until the other peer sends P2P_OVER + * after it got all elements from us. */ + } + } + if (PHASE_FINISH_CLOSING == op->state->phase) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "In PHASE_FINISH_CLOSING, pending %u demands\n", + num_demanded); + if (0 == num_demanded) + { + op->state->phase = PHASE_DONE; + send_client_done (op); + _GSS_operation_destroy2 (op); + } + } +} + + +/** + * Check an element message from a remote peer. + * + * @param cls the union operation + * @param emsg the message + */ +static int +check_union_p2p_elements (void *cls, + const struct GNUNET_SETU_ElementMessage *emsg) +{ + struct Operation *op = cls; + + if (0 == GNUNET_CONTAINER_multihashmap_size (op->state->demanded_hashes)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Handle an element message from a remote peer. + * Sent by the other peer either because we decoded an IBF and placed a demand, + * or because the other peer switched to full set transmission. + * + * @param cls the union operation + * @param emsg the message + */ +static void +handle_union_p2p_elements (void *cls, + const struct GNUNET_SETU_ElementMessage *emsg) +{ + struct Operation *op = cls; + struct ElementEntry *ee; + struct KeyEntry *ke; + uint16_t element_size; + + element_size = ntohs (emsg->header.size) - sizeof(struct + GNUNET_SETU_ElementMessage); + ee = GNUNET_malloc (sizeof(struct ElementEntry) + element_size); + GNUNET_memcpy (&ee[1], + &emsg[1], + element_size); + ee->element.size = element_size; + ee->element.data = &ee[1]; + ee->element.element_type = ntohs (emsg->element_type); + ee->remote = GNUNET_YES; + GNUNET_SETU_element_hash (&ee->element, + &ee->element_hash); + if (GNUNET_NO == + GNUNET_CONTAINER_multihashmap_remove (op->state->demanded_hashes, + &ee->element_hash, + NULL)) + { + /* We got something we didn't demand, since it's not in our map. */ + GNUNET_break_op (0); + fail_union_operation (op); + return; + } + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Got element (size %u, hash %s) from peer\n", + (unsigned int) element_size, + GNUNET_h2s (&ee->element_hash)); + + GNUNET_STATISTICS_update (_GSS_statistics, + "# received elements", + 1, + GNUNET_NO); + GNUNET_STATISTICS_update (_GSS_statistics, + "# exchanged elements", + 1, + GNUNET_NO); + + op->state->received_total++; + + ke = op_get_element (op, &ee->element_hash); + if (NULL != ke) + { + /* Got repeated element. Should not happen since + * we track demands. */ + GNUNET_STATISTICS_update (_GSS_statistics, + "# repeated elements", + 1, + GNUNET_NO); + ke->received = GNUNET_YES; + GNUNET_free (ee); + } + else + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Registering new element from remote peer\n"); + op->state->received_fresh++; + op_register_element (op, ee, GNUNET_YES); + /* only send results immediately if the client wants it */ + send_client_element (op, + &ee->element, + GNUNET_SETU_STATUS_ADD_LOCAL); + } + + if ((op->state->received_total > 8) && + (op->state->received_fresh < op->state->received_total / 3)) + { + /* The other peer gave us lots of old elements, there's something wrong. */ + GNUNET_break_op (0); + fail_union_operation (op); + return; + } + GNUNET_CADET_receive_done (op->channel); + maybe_finish (op); +} + + +/** + * Check a full element message from a remote peer. + * + * @param cls the union operation + * @param emsg the message + */ +static int +check_union_p2p_full_element (void *cls, + const struct GNUNET_SETU_ElementMessage *emsg) +{ + struct Operation *op = cls; + + (void) op; + // FIXME: check that we expect full elements here? + return GNUNET_OK; +} + + +/** + * Handle an element message from a remote peer. + * + * @param cls the union operation + * @param emsg the message + */ +static void +handle_union_p2p_full_element (void *cls, + const struct GNUNET_SETU_ElementMessage *emsg) +{ + struct Operation *op = cls; + struct ElementEntry *ee; + struct KeyEntry *ke; + uint16_t element_size; + + element_size = ntohs (emsg->header.size) + - sizeof(struct GNUNET_SETU_ElementMessage); + ee = GNUNET_malloc (sizeof(struct ElementEntry) + element_size); + GNUNET_memcpy (&ee[1], &emsg[1], element_size); + ee->element.size = element_size; + ee->element.data = &ee[1]; + ee->element.element_type = ntohs (emsg->element_type); + ee->remote = GNUNET_YES; + GNUNET_SETU_element_hash (&ee->element, &ee->element_hash); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Got element (full diff, size %u, hash %s) from peer\n", + (unsigned int) element_size, + GNUNET_h2s (&ee->element_hash)); + + GNUNET_STATISTICS_update (_GSS_statistics, + "# received elements", + 1, + GNUNET_NO); + GNUNET_STATISTICS_update (_GSS_statistics, + "# exchanged elements", + 1, + GNUNET_NO); + + op->state->received_total++; + + ke = op_get_element (op, &ee->element_hash); + if (NULL != ke) + { + /* Got repeated element. Should not happen since + * we track demands. */ + GNUNET_STATISTICS_update (_GSS_statistics, + "# repeated elements", + 1, + GNUNET_NO); + ke->received = GNUNET_YES; + GNUNET_free (ee); + } + else + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Registering new element from remote peer\n"); + op->state->received_fresh++; + op_register_element (op, ee, GNUNET_YES); + /* only send results immediately if the client wants it */ + send_client_element (op, + &ee->element, + GNUNET_SETU_STATUS_ADD_LOCAL); + } + + if ((GNUNET_YES == op->byzantine) && + (op->state->received_total > 384 + op->state->received_fresh * 4) && + (op->state->received_fresh < op->state->received_total / 6)) + { + /* The other peer gave us lots of old elements, there's something wrong. */ + LOG (GNUNET_ERROR_TYPE_ERROR, + "Other peer sent only %llu/%llu fresh elements, failing operation\n", + (unsigned long long) op->state->received_fresh, + (unsigned long long) op->state->received_total); + GNUNET_break_op (0); + fail_union_operation (op); + return; + } + GNUNET_CADET_receive_done (op->channel); +} + + +/** + * Send offers (for GNUNET_Hash-es) in response + * to inquiries (for IBF_Key-s). + * + * @param cls the union operation + * @param msg the message + */ +static int +check_union_p2p_inquiry (void *cls, + const struct InquiryMessage *msg) +{ + struct Operation *op = cls; + unsigned int num_keys; + + if (op->state->phase != PHASE_INVENTORY_PASSIVE) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + num_keys = (ntohs (msg->header.size) - sizeof(struct InquiryMessage)) + / sizeof(struct IBF_Key); + if ((ntohs (msg->header.size) - sizeof(struct InquiryMessage)) + != num_keys * sizeof(struct IBF_Key)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Send offers (for GNUNET_Hash-es) in response + * to inquiries (for IBF_Key-s). + * + * @param cls the union operation + * @param msg the message + */ +static void +handle_union_p2p_inquiry (void *cls, + const struct InquiryMessage *msg) +{ + struct Operation *op = cls; + const struct IBF_Key *ibf_key; + unsigned int num_keys; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received union inquiry\n"); + num_keys = (ntohs (msg->header.size) - sizeof(struct InquiryMessage)) + / sizeof(struct IBF_Key); + ibf_key = (const struct IBF_Key *) &msg[1]; + while (0 != num_keys--) + { + struct IBF_Key unsalted_key; + + unsalt_key (ibf_key, + ntohl (msg->salt), + &unsalted_key); + send_offers_for_key (op, + unsalted_key); + ibf_key++; + } + GNUNET_CADET_receive_done (op->channel); +} + + +/** + * Iterator over hash map entries, called to + * destroy the linked list of colliding ibf key entries. + * + * @param cls closure + * @param key current key code + * @param value value in the hash map + * @return #GNUNET_YES if we should continue to iterate, + * #GNUNET_NO if not. + */ +static int +send_missing_full_elements_iter (void *cls, + uint32_t key, + void *value) +{ + struct Operation *op = cls; + struct KeyEntry *ke = value; + struct GNUNET_MQ_Envelope *ev; + struct GNUNET_SETU_ElementMessage *emsg; + struct ElementEntry *ee = ke->element; + + if (GNUNET_YES == ke->received) + return GNUNET_YES; + ev = GNUNET_MQ_msg_extra (emsg, + ee->element.size, + GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_ELEMENT); + GNUNET_memcpy (&emsg[1], + ee->element.data, + ee->element.size); + emsg->element_type = htons (ee->element.element_type); + GNUNET_MQ_send (op->mq, + ev); + return GNUNET_YES; +} + + +/** + * Handle a request for full set transmission. + * + * @parem cls closure, a set union operation + * @param mh the demand message + */ +static void +handle_union_p2p_request_full (void *cls, + const struct GNUNET_MessageHeader *mh) +{ + struct Operation *op = cls; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received request for full set transmission\n"); + if (PHASE_EXPECT_IBF != op->state->phase) + { + GNUNET_break_op (0); + fail_union_operation (op); + return; + } + + // FIXME: we need to check that our set is larger than the + // byzantine_lower_bound by some threshold + send_full_set (op); + GNUNET_CADET_receive_done (op->channel); +} + + +/** + * Handle a "full done" message. + * + * @parem cls closure, a set union operation + * @param mh the demand message + */ +static void +handle_union_p2p_full_done (void *cls, + const struct GNUNET_MessageHeader *mh) +{ + struct Operation *op = cls; + + switch (op->state->phase) + { + case PHASE_EXPECT_IBF: + { + struct GNUNET_MQ_Envelope *ev; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "got FULL DONE, sending elements that other peer is missing\n"); + + /* send all the elements that did not come from the remote peer */ + GNUNET_CONTAINER_multihashmap32_iterate (op->state->key_to_element, + &send_missing_full_elements_iter, + op); + + ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_DONE); + GNUNET_MQ_send (op->mq, + ev); + op->state->phase = PHASE_DONE; + /* we now wait until the other peer sends us the OVER message*/ + } + break; + + case PHASE_FULL_SENDING: + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "got FULL DONE, finishing\n"); + /* We sent the full set, and got the response for that. We're done. */ + op->state->phase = PHASE_DONE; + GNUNET_CADET_receive_done (op->channel); + send_client_done (op); + _GSS_operation_destroy2 (op); + return; + } + break; + + default: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Handle full done phase is %u\n", + (unsigned) op->state->phase); + GNUNET_break_op (0); + fail_union_operation (op); + return; + } + GNUNET_CADET_receive_done (op->channel); +} + + +/** + * Check a demand by the other peer for elements based on a list + * of `struct GNUNET_HashCode`s. + * + * @parem cls closure, a set union operation + * @param mh the demand message + * @return #GNUNET_OK if @a mh is well-formed + */ +static int +check_union_p2p_demand (void *cls, + const struct GNUNET_MessageHeader *mh) +{ + struct Operation *op = cls; + unsigned int num_hashes; + + (void) op; + num_hashes = (ntohs (mh->size) - sizeof(struct GNUNET_MessageHeader)) + / sizeof(struct GNUNET_HashCode); + if ((ntohs (mh->size) - sizeof(struct GNUNET_MessageHeader)) + != num_hashes * sizeof(struct GNUNET_HashCode)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Handle a demand by the other peer for elements based on a list + * of `struct GNUNET_HashCode`s. + * + * @parem cls closure, a set union operation + * @param mh the demand message + */ +static void +handle_union_p2p_demand (void *cls, + const struct GNUNET_MessageHeader *mh) +{ + struct Operation *op = cls; + struct ElementEntry *ee; + struct GNUNET_SETU_ElementMessage *emsg; + const struct GNUNET_HashCode *hash; + unsigned int num_hashes; + struct GNUNET_MQ_Envelope *ev; + + num_hashes = (ntohs (mh->size) - sizeof(struct GNUNET_MessageHeader)) + / sizeof(struct GNUNET_HashCode); + for (hash = (const struct GNUNET_HashCode *) &mh[1]; + num_hashes > 0; + hash++, num_hashes--) + { + ee = GNUNET_CONTAINER_multihashmap_get (op->set->content->elements, + hash); + if (NULL == ee) + { + /* Demand for non-existing element. */ + GNUNET_break_op (0); + fail_union_operation (op); + return; + } + if (GNUNET_NO == _GSS_is_element_of_operation (ee, op)) + { + /* Probably confused lazily copied sets. */ + GNUNET_break_op (0); + fail_union_operation (op); + return; + } + ev = GNUNET_MQ_msg_extra (emsg, ee->element.size, + GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENTS); + GNUNET_memcpy (&emsg[1], ee->element.data, ee->element.size); + emsg->reserved = htons (0); + emsg->element_type = htons (ee->element.element_type); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "[OP %x] Sending demanded element (size %u, hash %s) to peer\n", + (void *) op, + (unsigned int) ee->element.size, + GNUNET_h2s (&ee->element_hash)); + GNUNET_MQ_send (op->mq, ev); + GNUNET_STATISTICS_update (_GSS_statistics, + "# exchanged elements", + 1, + GNUNET_NO); + } + GNUNET_CADET_receive_done (op->channel); +} + + +/** + * Check offer (of `struct GNUNET_HashCode`s). + * + * @param cls the union operation + * @param mh the message + * @return #GNUNET_OK if @a mh is well-formed + */ +static int +check_union_p2p_offer (void *cls, + const struct GNUNET_MessageHeader *mh) +{ + struct Operation *op = cls; + unsigned int num_hashes; + + /* look up elements and send them */ + if ((op->state->phase != PHASE_INVENTORY_PASSIVE) && + (op->state->phase != PHASE_INVENTORY_ACTIVE)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + num_hashes = (ntohs (mh->size) - sizeof(struct GNUNET_MessageHeader)) + / sizeof(struct GNUNET_HashCode); + if ((ntohs (mh->size) - sizeof(struct GNUNET_MessageHeader)) != + num_hashes * sizeof(struct GNUNET_HashCode)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Handle offers (of `struct GNUNET_HashCode`s) and + * respond with demands (of `struct GNUNET_HashCode`s). + * + * @param cls the union operation + * @param mh the message + */ +static void +handle_union_p2p_offer (void *cls, + const struct GNUNET_MessageHeader *mh) +{ + struct Operation *op = cls; + const struct GNUNET_HashCode *hash; + unsigned int num_hashes; + + num_hashes = (ntohs (mh->size) - sizeof(struct GNUNET_MessageHeader)) + / sizeof(struct GNUNET_HashCode); + for (hash = (const struct GNUNET_HashCode *) &mh[1]; + num_hashes > 0; + hash++, num_hashes--) + { + struct ElementEntry *ee; + struct GNUNET_MessageHeader *demands; + struct GNUNET_MQ_Envelope *ev; + + ee = GNUNET_CONTAINER_multihashmap_get (op->set->content->elements, + hash); + if (NULL != ee) + if (GNUNET_YES == _GSS_is_element_of_operation (ee, op)) + continue; + + if (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_contains (op->state->demanded_hashes, + hash)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Skipped sending duplicate demand\n"); + continue; + } + + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put ( + op->state->demanded_hashes, + hash, + NULL, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "[OP %x] Requesting element (hash %s)\n", + (void *) op, GNUNET_h2s (hash)); + ev = GNUNET_MQ_msg_header_extra (demands, + sizeof(struct GNUNET_HashCode), + GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DEMAND); + GNUNET_memcpy (&demands[1], + hash, + sizeof(struct GNUNET_HashCode)); + GNUNET_MQ_send (op->mq, ev); + } + GNUNET_CADET_receive_done (op->channel); +} + + +/** + * Handle a done message from a remote peer + * + * @param cls the union operation + * @param mh the message + */ +static void +handle_union_p2p_done (void *cls, + const struct GNUNET_MessageHeader *mh) +{ + struct Operation *op = cls; + + switch (op->state->phase) + { + case PHASE_INVENTORY_PASSIVE: + /* We got all requests, but still have to send our elements in response. */ + op->state->phase = PHASE_FINISH_WAITING; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "got DONE (as passive partner), waiting for our demands to be satisfied\n"); + /* The active peer is done sending offers + * and inquiries. This means that all + * our responses to that (demands and offers) + * must be in flight (queued or in mesh). + * + * We should notify the active peer once + * all our demands are satisfied, so that the active + * peer can quit if we gave it everything. + */GNUNET_CADET_receive_done (op->channel); + maybe_finish (op); + return; + + case PHASE_INVENTORY_ACTIVE: + LOG (GNUNET_ERROR_TYPE_DEBUG, + "got DONE (as active partner), waiting to finish\n"); + /* All demands of the other peer are satisfied, + * and we processed all offers, thus we know + * exactly what our demands must be. + * + * We'll close the channel + * to the other peer once our demands are met. + */op->state->phase = PHASE_FINISH_CLOSING; + GNUNET_CADET_receive_done (op->channel); + maybe_finish (op); + return; + + default: + GNUNET_break_op (0); + fail_union_operation (op); + return; + } +} + + +/** + * Handle a over message from a remote peer + * + * @param cls the union operation + * @param mh the message + */ +static void +handle_union_p2p_over (void *cls, + const struct GNUNET_MessageHeader *mh) +{ + send_client_done (cls); +} + + +/** + * Initiate operation to evaluate a set union with a remote peer. + * + * @param op operation to perform (to be initialized) + * @param opaque_context message to be transmitted to the listener + * to convince it to accept, may be NULL + */ +static struct OperationState * +union_evaluate (struct Operation *op, + const struct GNUNET_MessageHeader *opaque_context) +{ + struct OperationState *state; + struct GNUNET_MQ_Envelope *ev; + struct OperationRequestMessage *msg; + + ev = GNUNET_MQ_msg_nested_mh (msg, + GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST, + opaque_context); + if (NULL == ev) + { + /* the context message is too large */ + GNUNET_break (0); + return NULL; + } + state = GNUNET_new (struct OperationState); + state->demanded_hashes = GNUNET_CONTAINER_multihashmap_create (32, + GNUNET_NO); + /* copy the current generation's strata estimator for this operation */ + state->se = strata_estimator_dup (op->set->state->se); + /* we started the operation, thus we have to send the operation request */ + state->phase = PHASE_EXPECT_SE; + state->salt_receive = state->salt_send = 42; // FIXME????? + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Initiating union operation evaluation\n"); + GNUNET_STATISTICS_update (_GSS_statistics, + "# of total union operations", + 1, + GNUNET_NO); + GNUNET_STATISTICS_update (_GSS_statistics, + "# of initiated union operations", + 1, + GNUNET_NO); + GNUNET_MQ_send (op->mq, + ev); + + if (NULL != opaque_context) + LOG (GNUNET_ERROR_TYPE_DEBUG, + "sent op request with context message\n"); + else + LOG (GNUNET_ERROR_TYPE_DEBUG, + "sent op request without context message\n"); + + op->state = state; + initialize_key_to_element (op); + state->initial_size = GNUNET_CONTAINER_multihashmap32_size ( + state->key_to_element); + return state; +} + + +/** + * Get the incoming socket associated with the given id. + * + * @param listener the listener to look in + * @param id id to look for + * @return the incoming socket associated with the id, + * or NULL if there is none + */ +static struct Operation * +get_incoming (uint32_t id) +{ + for (struct Listener *listener = listener_head; + NULL != listener; + listener = listener->next) + { + for (struct Operation *op = listener->op_head; + NULL != op; + op = op->next) + if (op->suggest_id == id) + return op; + } + return NULL; +} + + +/** + * Destroy an incoming request from a remote peer + * + * @param op remote request to destroy + */ +static void +incoming_destroy (struct Operation *op) +{ + struct Listener *listener; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Destroying incoming operation %p\n", + op); + if (NULL != (listener = op->listener)) + { + GNUNET_CONTAINER_DLL_remove (listener->op_head, + listener->op_tail, + op); + op->listener = NULL; + } + if (NULL != op->timeout_task) + { + GNUNET_SCHEDULER_cancel (op->timeout_task); + op->timeout_task = NULL; + } + _GSS_operation_destroy2 (op); +} + + +/** + * Is element @a ee part of the set used by @a op? + * + * @param ee element to test + * @param op operation the defines the set and its generation + * @return #GNUNET_YES if the element is in the set, #GNUNET_NO if not + */ +int +_GSS_is_element_of_operation (struct ElementEntry *ee, + struct Operation *op) +{ + return ee->generation >= op->generation_created; +} + + +/** + * Destroy the given operation. Used for any operation where both + * peers were known and that thus actually had a vt and channel. Must + * not be used for operations where 'listener' is still set and we do + * not know the other peer. + * + * Call the implementation-specific cancel function of the operation. + * Disconnects from the remote peer. Does not disconnect the client, + * as there may be multiple operations per set. + * + * @param op operation to destroy + */ +void +_GSS_operation_destroy (struct Operation *op) +{ + struct Set *set = op->set; + struct GNUNET_CADET_Channel *channel; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Destroying operation %p\n", op); + GNUNET_assert (NULL == op->listener); + if (NULL != op->state) + { + union_op_cancel (op); + op->state = NULL; + } + if (NULL != set) + { + GNUNET_CONTAINER_DLL_remove (set->ops_head, + set->ops_tail, + op); + op->set = NULL; + } + if (NULL != op->context_msg) + { + GNUNET_free (op->context_msg); + op->context_msg = NULL; + } + if (NULL != (channel = op->channel)) + { + /* This will free op; called conditionally as this helper function + is also called from within the channel disconnect handler. */ + op->channel = NULL; + GNUNET_CADET_channel_destroy (channel); + } + /* We rely on the channel end handler to free 'op'. When 'op->channel' was NULL, + * there was a channel end handler that will free 'op' on the call stack. */ +} + + +/** + * Callback called when a client connects to the service. + * + * @param cls closure for the service + * @param c the new client that connected to the service + * @param mq the message queue used to send messages to the client + * @return @a `struct ClientState` + */ +static void * +client_connect_cb (void *cls, + struct GNUNET_SERVICE_Client *c, + struct GNUNET_MQ_Handle *mq) +{ + struct ClientState *cs; + + num_clients++; + cs = GNUNET_new (struct ClientState); + cs->client = c; + cs->mq = mq; + return cs; +} + + +/** + * Iterator over hash map entries to free element entries. + * + * @param cls closure + * @param key current key code + * @param value a `struct ElementEntry *` to be free'd + * @return #GNUNET_YES (continue to iterate) + */ +static int +destroy_elements_iterator (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct ElementEntry *ee = value; + + GNUNET_free (ee); + return GNUNET_YES; +} + + +/** + * Clean up after a client has disconnected + * + * @param cls closure, unused + * @param client the client to clean up after + * @param internal_cls the `struct ClientState` + */ +static void +client_disconnect_cb (void *cls, + struct GNUNET_SERVICE_Client *client, + void *internal_cls) +{ + struct ClientState *cs = internal_cls; + struct Operation *op; + struct Listener *listener; + struct Set *set; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client disconnected, cleaning up\n"); + if (NULL != (set = cs->set)) + { + struct SetContent *content = set->content; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Destroying client's set\n"); + /* Destroy pending set operations */ + while (NULL != set->ops_head) + _GSS_operation_destroy (set->ops_head); + + /* Destroy operation-specific state */ + GNUNET_assert (NULL != set->state); + if (NULL != set->state->se) + { + strata_estimator_destroy (set->state->se); + set->state->se = NULL; + } + GNUNET_free (set->state); + + /* free set content (or at least decrement RC) */ + set->content = NULL; + GNUNET_assert (0 != content->refcount); + content->refcount--; + if (0 == content->refcount) + { + GNUNET_assert (NULL != content->elements); + GNUNET_CONTAINER_multihashmap_iterate (content->elements, + &destroy_elements_iterator, + NULL); + GNUNET_CONTAINER_multihashmap_destroy (content->elements); + content->elements = NULL; + GNUNET_free (content); + } + GNUNET_free (set); + } + + if (NULL != (listener = cs->listener)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Destroying client's listener\n"); + GNUNET_CADET_close_port (listener->open_port); + listener->open_port = NULL; + while (NULL != (op = listener->op_head)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Destroying incoming operation `%u' from peer `%s'\n", + (unsigned int) op->client_request_id, + GNUNET_i2s (&op->peer)); + incoming_destroy (op); + } + GNUNET_CONTAINER_DLL_remove (listener_head, + listener_tail, + listener); + GNUNET_free (listener); + } + GNUNET_free (cs); + num_clients--; + if ( (GNUNET_YES == in_shutdown) && + (0 == num_clients) ) + { + if (NULL != cadet) + { + GNUNET_CADET_disconnect (cadet); + cadet = NULL; + } + } +} + + +/** + * Check a request for a set operation from another peer. + * + * @param cls the operation state + * @param msg the received message + * @return #GNUNET_OK if the channel should be kept alive, + * #GNUNET_SYSERR to destroy the channel + */ +static int +check_incoming_msg (void *cls, + const struct OperationRequestMessage *msg) +{ + struct Operation *op = cls; + struct Listener *listener = op->listener; + const struct GNUNET_MessageHeader *nested_context; + + /* double operation request */ + if (0 != op->suggest_id) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + /* This should be equivalent to the previous condition, but can't hurt to check twice */ + if (NULL == listener) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + nested_context = GNUNET_MQ_extract_nested_mh (msg); + if ((NULL != nested_context) && + (ntohs (nested_context->size) > GNUNET_SETU_CONTEXT_MESSAGE_MAX_SIZE)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Handle a request for a set operation from another peer. Checks if we + * have a listener waiting for such a request (and in that case initiates + * asking the listener about accepting the connection). If no listener + * is waiting, we queue the operation request in hope that a listener + * shows up soon (before timeout). + * + * This msg is expected as the first and only msg handled through the + * non-operation bound virtual table, acceptance of this operation replaces + * our virtual table and subsequent msgs would be routed differently (as + * we then know what type of operation this is). + * + * @param cls the operation state + * @param msg the received message + * @return #GNUNET_OK if the channel should be kept alive, + * #GNUNET_SYSERR to destroy the channel + */ +static void +handle_incoming_msg (void *cls, + const struct OperationRequestMessage *msg) +{ + struct Operation *op = cls; + struct Listener *listener = op->listener; + const struct GNUNET_MessageHeader *nested_context; + struct GNUNET_MQ_Envelope *env; + struct GNUNET_SETU_RequestMessage *cmsg; + + nested_context = GNUNET_MQ_extract_nested_mh (msg); + /* Make a copy of the nested_context (application-specific context + information that is opaque to set) so we can pass it to the + listener later on */ + if (NULL != nested_context) + op->context_msg = GNUNET_copy_message (nested_context); + op->remote_element_count = ntohl (msg->element_count); + GNUNET_log ( + GNUNET_ERROR_TYPE_DEBUG, + "Received P2P operation request (port %s) for active listener\n", + GNUNET_h2s (&op->listener->app_id)); + GNUNET_assert (0 == op->suggest_id); + if (0 == suggest_id) + suggest_id++; + op->suggest_id = suggest_id++; + GNUNET_assert (NULL != op->timeout_task); + GNUNET_SCHEDULER_cancel (op->timeout_task); + op->timeout_task = NULL; + env = GNUNET_MQ_msg_nested_mh (cmsg, + GNUNET_MESSAGE_TYPE_SETU_REQUEST, + op->context_msg); + GNUNET_log ( + GNUNET_ERROR_TYPE_DEBUG, + "Suggesting incoming request with accept id %u to listener %p of client %p\n", + op->suggest_id, + listener, + listener->cs); + cmsg->accept_id = htonl (op->suggest_id); + cmsg->peer_id = op->peer; + GNUNET_MQ_send (listener->cs->mq, + env); + /* NOTE: GNUNET_CADET_receive_done() will be called in + #handle_client_accept() */ +} + + +/** + * Called when a client wants to create a new set. This is typically + * the first request from a client, and includes the type of set + * operation to be performed. + * + * @param cls client that sent the message + * @param m message sent by the client + */ +static void +handle_client_create_set (void *cls, + const struct GNUNET_SETU_CreateMessage *msg) +{ + struct ClientState *cs = cls; + struct Set *set; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client created new set for union operation\n"); + if (NULL != cs->set) + { + /* There can only be one set per client */ + GNUNET_break (0); + GNUNET_SERVICE_client_drop (cs->client); + return; + } + set = GNUNET_new (struct Set); + { + struct SetState *set_state; + + set_state = GNUNET_new (struct SetState); // FIXME: avoid this malloc, merge structs! + set_state->se = strata_estimator_create (SE_STRATA_COUNT, + SE_IBF_SIZE, SE_IBF_HASH_NUM); + if (NULL == set_state->se) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to allocate strata estimator\n"); + GNUNET_free (set); + GNUNET_SERVICE_client_drop (cs->client); + return; + } + set->state = set_state; + } + set->content = GNUNET_new (struct SetContent); + set->content->refcount = 1; + set->content->elements = GNUNET_CONTAINER_multihashmap_create (1, + GNUNET_YES); + set->cs = cs; + cs->set = set; + GNUNET_SERVICE_client_continue (cs->client); +} + + +/** + * Timeout happens iff: + * - we suggested an operation to our listener, + * but did not receive a response in time + * - we got the channel from a peer but no #GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST + * + * @param cls channel context + * @param tc context information (why was this task triggered now) + */ +static void +incoming_timeout_cb (void *cls) +{ + struct Operation *op = cls; + + op->timeout_task = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Remote peer's incoming request timed out\n"); + incoming_destroy (op); +} + + +/** + * Method called whenever another peer has added us to a channel the + * other peer initiated. Only called (once) upon reception of data + * from a channel we listen on. + * + * The channel context represents the operation itself and gets added + * to a DLL, from where it gets looked up when our local listener + * client responds to a proposed/suggested operation or connects and + * associates with this operation. + * + * @param cls closure + * @param channel new handle to the channel + * @param source peer that started the channel + * @return initial channel context for the channel + * returns NULL on error + */ +static void * +channel_new_cb (void *cls, + struct GNUNET_CADET_Channel *channel, + const struct GNUNET_PeerIdentity *source) +{ + struct Listener *listener = cls; + struct Operation *op; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "New incoming channel\n"); + op = GNUNET_new (struct Operation); + op->listener = listener; + op->peer = *source; + op->channel = channel; + op->mq = GNUNET_CADET_get_mq (op->channel); + op->salt = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, + UINT32_MAX); + op->timeout_task = GNUNET_SCHEDULER_add_delayed (INCOMING_CHANNEL_TIMEOUT, + &incoming_timeout_cb, + op); + GNUNET_CONTAINER_DLL_insert (listener->op_head, + listener->op_tail, + op); + return op; +} + + +/** + * Function called whenever a channel is destroyed. Should clean up + * any associated state. It must NOT call + * GNUNET_CADET_channel_destroy() on the channel. + * + * The peer_disconnect function is part of a a virtual table set initially either + * when a peer creates a new channel with us, or once we create + * a new channel ourselves (evaluate). + * + * Once we know the exact type of operation (union/intersection), the vt is + * replaced with an operation specific instance (_GSS_[op]_vt). + * + * @param channel_ctx place where local state associated + * with the channel is stored + * @param channel connection to the other end (henceforth invalid) + */ +static void +channel_end_cb (void *channel_ctx, + const struct GNUNET_CADET_Channel *channel) +{ + struct Operation *op = channel_ctx; + + op->channel = NULL; + _GSS_operation_destroy2 (op); +} + + +/** + * This function probably should not exist + * and be replaced by inlining more specific + * logic in the various places where it is called. + */ +void +_GSS_operation_destroy2 (struct Operation *op) +{ + struct GNUNET_CADET_Channel *channel; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "channel_end_cb called\n"); + if (NULL != (channel = op->channel)) + { + /* This will free op; called conditionally as this helper function + is also called from within the channel disconnect handler. */ + op->channel = NULL; + GNUNET_CADET_channel_destroy (channel); + } + if (NULL != op->listener) + { + incoming_destroy (op); + return; + } + if (NULL != op->set) + send_client_done (op); + _GSS_operation_destroy (op); + GNUNET_free (op); +} + + +/** + * Function called whenever an MQ-channel's transmission window size changes. + * + * The first callback in an outgoing channel will be with a non-zero value + * and will mean the channel is connected to the destination. + * + * For an incoming channel it will be called immediately after the + * #GNUNET_CADET_ConnectEventHandler, also with a non-zero value. + * + * @param cls Channel closure. + * @param channel Connection to the other end (henceforth invalid). + * @param window_size New window size. If the is more messages than buffer size + * this value will be negative.. + */ +static void +channel_window_cb (void *cls, + const struct GNUNET_CADET_Channel *channel, + int window_size) +{ + /* FIXME: not implemented, we could do flow control here... */ +} + + +/** + * Called when a client wants to create a new listener. + * + * @param cls client that sent the message + * @param msg message sent by the client + */ +static void +handle_client_listen (void *cls, + const struct GNUNET_SETU_ListenMessage *msg) +{ + struct ClientState *cs = cls; + struct GNUNET_MQ_MessageHandler cadet_handlers[] = { + GNUNET_MQ_hd_var_size (incoming_msg, + GNUNET_MESSAGE_TYPE_SETU_P2P_OPERATION_REQUEST, + struct OperationRequestMessage, + NULL), + GNUNET_MQ_hd_var_size (union_p2p_ibf, + GNUNET_MESSAGE_TYPE_SETU_P2P_IBF, + struct IBFMessage, + NULL), + GNUNET_MQ_hd_var_size (union_p2p_elements, + GNUNET_MESSAGE_TYPE_SETU_P2P_ELEMENTS, + struct GNUNET_SETU_ElementMessage, + NULL), + GNUNET_MQ_hd_var_size (union_p2p_offer, + GNUNET_MESSAGE_TYPE_SETU_P2P_OFFER, + struct GNUNET_MessageHeader, + NULL), + GNUNET_MQ_hd_var_size (union_p2p_inquiry, + GNUNET_MESSAGE_TYPE_SETU_P2P_INQUIRY, + struct InquiryMessage, + NULL), + GNUNET_MQ_hd_var_size (union_p2p_demand, + GNUNET_MESSAGE_TYPE_SETU_P2P_DEMAND, + struct GNUNET_MessageHeader, + NULL), + GNUNET_MQ_hd_fixed_size (union_p2p_done, + GNUNET_MESSAGE_TYPE_SETU_P2P_DONE, + struct GNUNET_MessageHeader, + NULL), + GNUNET_MQ_hd_fixed_size (union_p2p_over, + GNUNET_MESSAGE_TYPE_SETU_P2P_OVER, + struct GNUNET_MessageHeader, + NULL), + GNUNET_MQ_hd_fixed_size (union_p2p_full_done, + GNUNET_MESSAGE_TYPE_SETU_P2P_FULL_DONE, + struct GNUNET_MessageHeader, + NULL), + GNUNET_MQ_hd_fixed_size (union_p2p_request_full, + GNUNET_MESSAGE_TYPE_SETU_P2P_REQUEST_FULL, + struct GNUNET_MessageHeader, + NULL), + GNUNET_MQ_hd_var_size (union_p2p_strata_estimator, + GNUNET_MESSAGE_TYPE_SETU_P2P_SE, + struct StrataEstimatorMessage, + NULL), + GNUNET_MQ_hd_var_size (union_p2p_strata_estimator, + GNUNET_MESSAGE_TYPE_SETU_P2P_SEC, + struct StrataEstimatorMessage, + NULL), + GNUNET_MQ_hd_var_size (union_p2p_full_element, + GNUNET_MESSAGE_TYPE_SETU_P2P_FULL_ELEMENT, + struct GNUNET_SETU_ElementMessage, + NULL), + GNUNET_MQ_handler_end () + }; + struct Listener *listener; + + if (NULL != cs->listener) + { + /* max. one active listener per client! */ + GNUNET_break (0); + GNUNET_SERVICE_client_drop (cs->client); + return; + } + listener = GNUNET_new (struct Listener); + listener->cs = cs; + cs->listener = listener; + listener->app_id = msg->app_id; + GNUNET_CONTAINER_DLL_insert (listener_head, + listener_tail, + listener); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "New listener created (port %s)\n", + GNUNET_h2s (&listener->app_id)); + listener->open_port = GNUNET_CADET_open_port (cadet, + &msg->app_id, + &channel_new_cb, + listener, + &channel_window_cb, + &channel_end_cb, + cadet_handlers); + GNUNET_SERVICE_client_continue (cs->client); +} + + +/** + * Called when the listening client rejects an operation + * request by another peer. + * + * @param cls client that sent the message + * @param msg message sent by the client + */ +static void +handle_client_reject (void *cls, + const struct GNUNET_SETU_RejectMessage *msg) +{ + struct ClientState *cs = cls; + struct Operation *op; + + op = get_incoming (ntohl (msg->accept_reject_id)); + if (NULL == op) + { + /* no matching incoming operation for this reject; + could be that the other peer already disconnected... */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Client rejected unknown operation %u\n", + (unsigned int) ntohl (msg->accept_reject_id)); + GNUNET_SERVICE_client_continue (cs->client); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Peer request (app %s) rejected by client\n", + GNUNET_h2s (&cs->listener->app_id)); + _GSS_operation_destroy2 (op); + GNUNET_SERVICE_client_continue (cs->client); +} + + +/** + * Called when a client wants to add or remove an element to a set it inhabits. + * + * @param cls client that sent the message + * @param msg message sent by the client + */ +static int +check_client_set_add (void *cls, + const struct GNUNET_SETU_ElementMessage *msg) +{ + /* NOTE: Technically, we should probably check with the + block library whether the element we are given is well-formed */ + return GNUNET_OK; +} + + +/** + * Called when a client wants to add or remove an element to a set it inhabits. + * + * @param cls client that sent the message + * @param msg message sent by the client + */ +static void +handle_client_set_add (void *cls, + const struct GNUNET_SETU_ElementMessage *msg) +{ + struct ClientState *cs = cls; + struct Set *set; + struct GNUNET_SETU_Element el; + struct ElementEntry *ee; + struct GNUNET_HashCode hash; + + if (NULL == (set = cs->set)) + { + /* client without a set requested an operation */ + GNUNET_break (0); + GNUNET_SERVICE_client_drop (cs->client); + return; + } + GNUNET_SERVICE_client_continue (cs->client); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Executing mutation on set\n"); + GNUNET_assert (GNUNET_MESSAGE_TYPE_SET_ADD == ntohs (msg->header.type)); + el.size = ntohs (msg->header.size) - sizeof(*msg); + el.data = &msg[1]; + el.element_type = ntohs (msg->element_type); + GNUNET_SETU_element_hash (&el, + &hash); + ee = GNUNET_CONTAINER_multihashmap_get (set->content->elements, + &hash); + if (NULL == ee) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client inserts element %s of size %u\n", + GNUNET_h2s (&hash), + el.size); + ee = GNUNET_malloc (el.size + sizeof(*ee)); + ee->element.size = el.size; + GNUNET_memcpy (&ee[1], el.data, el.size); + ee->element.data = &ee[1]; + ee->element.element_type = el.element_type; + ee->remote = GNUNET_NO; + ee->generation = set->current_generation; + ee->element_hash = hash; + GNUNET_break (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_put ( + set->content->elements, + &ee->element_hash, + ee, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client inserted element %s of size %u twice (ignored)\n", + GNUNET_h2s (&hash), + el.size); + /* same element inserted twice */ + return; + } + strata_estimator_insert (set->state->se, + get_ibf_key (&ee->element_hash)); +} + + +/** + * Advance the current generation of a set, + * adding exclusion ranges if necessary. + * + * @param set the set where we want to advance the generation + */ +static void +advance_generation (struct Set *set) +{ + set->content->latest_generation++; + set->current_generation++; +} + + +/** + * Called when a client wants to initiate a set operation with another + * peer. Initiates the CADET connection to the listener and sends the + * request. + * + * @param cls client that sent the message + * @param msg message sent by the client + * @return #GNUNET_OK if the message is well-formed + */ +static int +check_client_evaluate (void *cls, + const struct GNUNET_SETU_EvaluateMessage *msg) +{ + /* FIXME: suboptimal, even if the context below could be NULL, + there are malformed messages this does not check for... */ + return GNUNET_OK; +} + + +/** + * Called when a client wants to initiate a set operation with another + * peer. Initiates the CADET connection to the listener and sends the + * request. + * + * @param cls client that sent the message + * @param msg message sent by the client + */ +static void +handle_client_evaluate (void *cls, + const struct GNUNET_SETU_EvaluateMessage *msg) +{ + struct ClientState *cs = cls; + struct Operation *op = GNUNET_new (struct Operation); + const struct GNUNET_MQ_MessageHandler cadet_handlers[] = { + GNUNET_MQ_hd_var_size (incoming_msg, + GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST, + struct OperationRequestMessage, + op), + GNUNET_MQ_hd_var_size (union_p2p_ibf, + GNUNET_MESSAGE_TYPE_SET_UNION_P2P_IBF, + struct IBFMessage, + op), + GNUNET_MQ_hd_var_size (union_p2p_elements, + GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENTS, + struct GNUNET_SETU_ElementMessage, + op), + GNUNET_MQ_hd_var_size (union_p2p_offer, + GNUNET_MESSAGE_TYPE_SET_UNION_P2P_OFFER, + struct GNUNET_MessageHeader, + op), + GNUNET_MQ_hd_var_size (union_p2p_inquiry, + GNUNET_MESSAGE_TYPE_SET_UNION_P2P_INQUIRY, + struct InquiryMessage, + op), + GNUNET_MQ_hd_var_size (union_p2p_demand, + GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DEMAND, + struct GNUNET_MessageHeader, + op), + GNUNET_MQ_hd_fixed_size (union_p2p_done, + GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DONE, + struct GNUNET_MessageHeader, + op), + GNUNET_MQ_hd_fixed_size (union_p2p_over, + GNUNET_MESSAGE_TYPE_SET_UNION_P2P_OVER, + struct GNUNET_MessageHeader, + op), + GNUNET_MQ_hd_fixed_size (union_p2p_full_done, + GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_DONE, + struct GNUNET_MessageHeader, + op), + GNUNET_MQ_hd_fixed_size (union_p2p_request_full, + GNUNET_MESSAGE_TYPE_SET_UNION_P2P_REQUEST_FULL, + struct GNUNET_MessageHeader, + op), + GNUNET_MQ_hd_var_size (union_p2p_strata_estimator, + GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SE, + struct StrataEstimatorMessage, + op), + GNUNET_MQ_hd_var_size (union_p2p_strata_estimator, + GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SEC, + struct StrataEstimatorMessage, + op), + GNUNET_MQ_hd_var_size (union_p2p_full_element, + GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_ELEMENT, + struct GNUNET_SETU_ElementMessage, + op), + GNUNET_MQ_handler_end () + }; + struct Set *set; + const struct GNUNET_MessageHeader *context; + + if (NULL == (set = cs->set)) + { + GNUNET_break (0); + GNUNET_free (op); + GNUNET_SERVICE_client_drop (cs->client); + return; + } + op->salt = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, + UINT32_MAX); + op->peer = msg->target_peer; + op->client_request_id = ntohl (msg->request_id); + op->byzantine = msg->byzantine; + op->byzantine_lower_bound = msg->byzantine_lower_bound; + op->force_full = msg->force_full; + op->force_delta = msg->force_delta; + context = GNUNET_MQ_extract_nested_mh (msg); + + /* Advance generation values, so that + mutations won't interfer with the running operation. */ + op->set = set; + op->generation_created = set->current_generation; + advance_generation (set); + GNUNET_CONTAINER_DLL_insert (set->ops_head, + set->ops_tail, + op); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Creating new CADET channel to port %s for set union\n", + GNUNET_h2s (&msg->app_id)); + op->channel = GNUNET_CADET_channel_create (cadet, + op, + &msg->target_peer, + &msg->app_id, + &channel_window_cb, + &channel_end_cb, + cadet_handlers); + op->mq = GNUNET_CADET_get_mq (op->channel); + op->state = union_evaluate (op, context); + if (NULL == op->state) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (cs->client); + return; + } + GNUNET_SERVICE_client_continue (cs->client); +} + + +/** + * Handle a request from the client to cancel a running set operation. + * + * @param cls the client + * @param msg the message + */ +static void +handle_client_cancel (void *cls, + const struct GNUNET_SETU_CancelMessage *msg) +{ + struct ClientState *cs = cls; + struct Set *set; + struct Operation *op; + int found; + + if (NULL == (set = cs->set)) + { + /* client without a set requested an operation */ + GNUNET_break (0); + GNUNET_SERVICE_client_drop (cs->client); + return; + } + found = GNUNET_NO; + for (op = set->ops_head; NULL != op; op = op->next) + { + if (op->client_request_id == ntohl (msg->request_id)) + { + found = GNUNET_YES; + break; + } + } + if (GNUNET_NO == found) + { + /* It may happen that the operation was already destroyed due to + * the other peer disconnecting. The client may not know about this + * yet and try to cancel the (just barely non-existent) operation. + * So this is not a hard error. + */// + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Client canceled non-existent op %u\n", + (uint32_t) ntohl (msg->request_id)); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client requested cancel for op %u\n", + (uint32_t) ntohl (msg->request_id)); + _GSS_operation_destroy (op); + } + GNUNET_SERVICE_client_continue (cs->client); +} + + +/** + * Handle a request from the client to accept a set operation that + * came from a remote peer. We forward the accept to the associated + * operation for handling + * + * @param cls the client + * @param msg the message + */ +static void +handle_client_accept (void *cls, + const struct GNUNET_SETU_AcceptMessage *msg) +{ + struct ClientState *cs = cls; + struct Set *set; + struct Operation *op; + struct GNUNET_SETU_ResultMessage *result_message; + struct GNUNET_MQ_Envelope *ev; + struct Listener *listener; + + if (NULL == (set = cs->set)) + { + /* client without a set requested to accept */ + GNUNET_break (0); + GNUNET_SERVICE_client_drop (cs->client); + return; + } + op = get_incoming (ntohl (msg->accept_reject_id)); + if (NULL == op) + { + /* It is not an error if the set op does not exist -- it may + * have been destroyed when the partner peer disconnected. */ + GNUNET_log ( + GNUNET_ERROR_TYPE_INFO, + "Client %p accepted request %u of listener %p that is no longer active\n", + cs, + ntohl (msg->accept_reject_id), + cs->listener); + ev = GNUNET_MQ_msg (result_message, + GNUNET_MESSAGE_TYPE_SET_RESULT); + result_message->request_id = msg->request_id; + result_message->result_status = htons (GNUNET_SETU_STATUS_FAILURE); + GNUNET_MQ_send (set->cs->mq, ev); + GNUNET_SERVICE_client_continue (cs->client); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client accepting request %u\n", + (uint32_t) ntohl (msg->accept_reject_id)); + listener = op->listener; + op->listener = NULL; + GNUNET_CONTAINER_DLL_remove (listener->op_head, + listener->op_tail, + op); + op->set = set; + GNUNET_CONTAINER_DLL_insert (set->ops_head, + set->ops_tail, + op); + op->client_request_id = ntohl (msg->request_id); + op->byzantine = msg->byzantine; + op->byzantine_lower_bound = msg->byzantine_lower_bound; + op->force_full = msg->force_full; + op->force_delta = msg->force_delta; + + /* Advance generation values, so that future mutations do not + interfer with the running operation. */ + op->generation_created = set->current_generation; + advance_generation (set); + GNUNET_assert (NULL == op->state); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "accepting set union operation\n"); + GNUNET_STATISTICS_update (_GSS_statistics, + "# of accepted union operations", + 1, + GNUNET_NO); + GNUNET_STATISTICS_update (_GSS_statistics, + "# of total union operations", + 1, + GNUNET_NO); + { + struct OperationState *state; + const struct StrataEstimator *se; + struct GNUNET_MQ_Envelope *ev; + struct StrataEstimatorMessage *strata_msg; + char *buf; + size_t len; + uint16_t type; + + state = GNUNET_new (struct OperationState); // FIXME: merge with 'op' to avoid malloc! + state->se = strata_estimator_dup (op->set->state->se); + state->demanded_hashes = GNUNET_CONTAINER_multihashmap_create (32, + GNUNET_NO); + state->salt_receive = state->salt_send = 42; // FIXME????? + op->state = state; + initialize_key_to_element (op); + state->initial_size = GNUNET_CONTAINER_multihashmap32_size ( + state->key_to_element); + + /* kick off the operation */ + se = state->se; + buf = GNUNET_malloc (se->strata_count * IBF_BUCKET_SIZE * se->ibf_size); + len = strata_estimator_write (se, + buf); + if (len < se->strata_count * IBF_BUCKET_SIZE * se->ibf_size) + type = GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SEC; + else + type = GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SE; + ev = GNUNET_MQ_msg_extra (strata_msg, + len, + type); + GNUNET_memcpy (&strata_msg[1], + buf, + len); + GNUNET_free (buf); + strata_msg->set_size + = GNUNET_htonll (GNUNET_CONTAINER_multihashmap_size ( + op->set->content->elements)); + GNUNET_MQ_send (op->mq, + ev); + state->phase = PHASE_EXPECT_IBF; + + op->state = state; + } + /* Now allow CADET to continue, as we did not do this in + #handle_incoming_msg (as we wanted to first see if the + local client would accept the request). */ + GNUNET_CADET_receive_done (op->channel); + GNUNET_SERVICE_client_continue (cs->client); +} + + +/** + * Called to clean up, after a shutdown has been requested. + * + * @param cls closure, NULL + */ +static void +shutdown_task (void *cls) +{ + /* Delay actual shutdown to allow service to disconnect clients */ + in_shutdown = GNUNET_YES; + if (0 == num_clients) + { + if (NULL != cadet) + { + GNUNET_CADET_disconnect (cadet); + cadet = NULL; + } + } + GNUNET_STATISTICS_destroy (_GSS_statistics, + GNUNET_YES); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "handled shutdown request\n"); +} + + +/** + * Function called by the service's run + * method to run service-specific setup code. + * + * @param cls closure + * @param cfg configuration to use + * @param service the initialized service + */ +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_SERVICE_Handle *service) +{ + /* FIXME: need to modify SERVICE (!) API to allow + us to run a shutdown task *after* clients were + forcefully disconnected! */ + GNUNET_SCHEDULER_add_shutdown (&shutdown_task, + NULL); + _GSS_statistics = GNUNET_STATISTICS_create ("setu", cfg); + cadet = GNUNET_CADET_connect (cfg); + if (NULL == cadet) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Could not connect to CADET service\n")); + GNUNET_SCHEDULER_shutdown (); + return; + } +} + + +/** + * Define "main" method using service macro. + */ +GNUNET_SERVICE_MAIN ( + "set", + GNUNET_SERVICE_OPTION_NONE, + &run, + &client_connect_cb, + &client_disconnect_cb, + NULL, + GNUNET_MQ_hd_fixed_size (client_accept, + GNUNET_MESSAGE_TYPE_SETU_ACCEPT, + struct GNUNET_SETU_AcceptMessage, + NULL), + GNUNET_MQ_hd_var_size (client_set_add, + GNUNET_MESSAGE_TYPE_SETU_ADD, + struct GNUNET_SETU_ElementMessage, + NULL), + GNUNET_MQ_hd_fixed_size (client_create_set, + GNUNET_MESSAGE_TYPE_SETU_CREATE, + struct GNUNET_SETU_CreateMessage, + NULL), + GNUNET_MQ_hd_var_size (client_evaluate, + GNUNET_MESSAGE_TYPE_SETU_EVALUATE, + struct GNUNET_SETU_EvaluateMessage, + NULL), + GNUNET_MQ_hd_fixed_size (client_listen, + GNUNET_MESSAGE_TYPE_SETU_LISTEN, + struct GNUNET_SETU_ListenMessage, + NULL), + GNUNET_MQ_hd_fixed_size (client_reject, + GNUNET_MESSAGE_TYPE_SETU_REJECT, + struct GNUNET_SETU_RejectMessage, + NULL), + GNUNET_MQ_hd_fixed_size (client_cancel, + GNUNET_MESSAGE_TYPE_SETU_CANCEL, + struct GNUNET_SETU_CancelMessage, + NULL), + GNUNET_MQ_handler_end ()); + + +/* end of gnunet-service-setu.c */ diff --git a/src/setu/gnunet-service-setu.h b/src/setu/gnunet-service-setu.h new file mode 100644 index 000000000..eb6b7a8e5 --- /dev/null +++ b/src/setu/gnunet-service-setu.h @@ -0,0 +1,393 @@ +/* + This file is part of GNUnet + Copyright (C) 2013-2017 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file set/gnunet-service-setu.h + * @brief common components for the implementation the different set operations + * @author Florian Dold + * @author Christian Grothoff + */ +#ifndef GNUNET_SERVICE_SETU_H_PRIVATE +#define GNUNET_SERVICE_SETU_H_PRIVATE + +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_applications.h" +#include "gnunet_core_service.h" +#include "gnunet_cadet_service.h" +#include "gnunet_setu_service.h" +#include "setu.h" + + +/** + * Implementation-specific set state. Used as opaque pointer, and + * specified further in the respective implementation. + */ +struct SetState; + +/** + * Implementation-specific set operation. Used as opaque pointer, and + * specified further in the respective implementation. + */ +struct OperationState; + +/** + * A set that supports a specific operation with other peers. + */ +struct Set; + +/** + * Information about an element element in the set. All elements are + * stored in a hash-table from their hash-code to their 'struct + * Element', so that the remove and add operations are reasonably + * fast. + */ +struct ElementEntry; + +/** + * Operation context used to execute a set operation. + */ +struct Operation; + + +/** + * Information about an element element in the set. All elements are + * stored in a hash-table from their hash-code to their `struct + * Element`, so that the remove and add operations are reasonably + * fast. + */ +struct ElementEntry +{ + /** + * The actual element. The data for the element + * should be allocated at the end of this struct. + */ + struct GNUNET_SETU_Element element; + + /** + * Hash of the element. For set union: Will be used to derive the + * different IBF keys for different salts. + */ + struct GNUNET_HashCode element_hash; + + /** + * First generation that includes this element. + */ + unsigned int generation; + + /** + * #GNUNET_YES if the element is a remote element, and does not belong + * to the operation's set. + */ + int remote; +}; + + +/** + * A listener is inhabited by a client, and waits for evaluation + * requests from remote peers. + */ +struct Listener; + + +/** + * State we keep per client. + */ +struct ClientState +{ + /** + * Set, if associated with the client, otherwise NULL. + */ + struct Set *set; + + /** + * Listener, if associated with the client, otherwise NULL. + */ + struct Listener *listener; + + /** + * Client handle. + */ + struct GNUNET_SERVICE_Client *client; + + /** + * Message queue. + */ + struct GNUNET_MQ_Handle *mq; +}; + + +/** + * Operation context used to execute a set operation. + */ +struct Operation +{ + /** + * Kept in a DLL of the listener, if @e listener is non-NULL. + */ + struct Operation *next; + + /** + * Kept in a DLL of the listener, if @e listener is non-NULL. + */ + struct Operation *prev; + + /** + * Channel to the peer. + */ + struct GNUNET_CADET_Channel *channel; + + /** + * Port this operation runs on. + */ + struct Listener *listener; + + /** + * Message queue for the channel. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Context message, may be NULL. + */ + struct GNUNET_MessageHeader *context_msg; + + /** + * Set associated with the operation, NULL until the spec has been + * associated with a set. + */ + struct Set *set; + + /** + * Operation-specific operation state. Note that the exact + * type depends on this being a union or intersection operation + * (and thus on @e vt). + */ + struct OperationState *state; + + /** + * The identity of the requesting peer. Needs to + * be stored here as the op spec might not have been created yet. + */ + struct GNUNET_PeerIdentity peer; + + /** + * Timeout task, if the incoming peer has not been accepted + * after the timeout, it will be disconnected. + */ + struct GNUNET_SCHEDULER_Task *timeout_task; + + /** + * Salt to use for the operation. + */ + uint32_t salt; + + /** + * Remote peers element count + */ + uint32_t remote_element_count; + + /** + * ID used to identify an operation between service and client + */ + uint32_t client_request_id; + + /** + * Always use delta operation instead of sending full sets, + * even it it's less efficient. + */ + int force_delta; + + /** + * Always send full sets, even if delta operations would + * be more efficient. + */ + int force_full; + + /** + * #GNUNET_YES to fail operations where Byzantine faults + * are suspected + */ + int byzantine; + + /** + * Lower bound for the set size, used only when + * byzantine mode is enabled. + */ + int byzantine_lower_bound; + + /** + * Unique request id for the request from a remote peer, sent to the + * client, which will accept or reject the request. Set to '0' iff + * the request has not been suggested yet. + */ + uint32_t suggest_id; + + /** + * Generation in which the operation handle + * was created. + */ + unsigned int generation_created; +}; + + +/** + * SetContent stores the actual set elements, which may be shared by + * multiple generations derived from one set. + */ +struct SetContent +{ + /** + * Maps `struct GNUNET_HashCode *` to `struct ElementEntry *`. + */ + struct GNUNET_CONTAINER_MultiHashMap *elements; + + /** + * Number of references to the content. + */ + unsigned int refcount; + + /** + * FIXME: document! + */ + unsigned int latest_generation; + + /** + * Number of concurrently active iterators. + */ + int iterator_count; +}; + + +struct GenerationRange +{ + /** + * First generation that is excluded. + */ + unsigned int start; + + /** + * Generation after the last excluded generation. + */ + unsigned int end; +}; + + +/** + * A set that supports a specific operation with other peers. + */ +struct Set +{ + /** + * Sets are held in a doubly linked list (in `sets_head` and `sets_tail`). + */ + struct Set *next; + + /** + * Sets are held in a doubly linked list. + */ + struct Set *prev; + + /** + * Client that owns the set. Only one client may own a set, + * and there can only be one set per client. + */ + struct ClientState *cs; + + /** + * Content, possibly shared by multiple sets, + * and thus reference counted. + */ + struct SetContent *content; + + /** + * Implementation-specific state. + */ + struct SetState *state; + + /** + * Evaluate operations are held in a linked list. + */ + struct Operation *ops_head; + + /** + * Evaluate operations are held in a linked list. + */ + struct Operation *ops_tail; + + /** + * Current generation, that is, number of previously executed + * operations and lazy copies on the underlying set content. + */ + unsigned int current_generation; + +}; + + +extern struct GNUNET_STATISTICS_Handle *_GSS_statistics; + + +/** + * Destroy the given operation. Used for any operation where both + * peers were known and that thus actually had a vt and channel. Must + * not be used for operations where 'listener' is still set and we do + * not know the other peer. + * + * Call the implementation-specific cancel function of the operation. + * Disconnects from the remote peer. Does not disconnect the client, + * as there may be multiple operations per set. + * + * @param op operation to destroy + */ +void +_GSS_operation_destroy (struct Operation *op); + + +/** + * This function probably should not exist + * and be replaced by inlining more specific + * logic in the various places where it is called. + */ +void +_GSS_operation_destroy2 (struct Operation *op); + + +/** + * Get the table with implementing functions for set union. + * + * @return the operation specific VTable + */ +const struct SetVT * +_GSS_union_vt (void); + + +/** + * Is element @a ee part of the set used by @a op? + * + * @param ee element to test + * @param op operation the defines the set and its generation + * @return #GNUNET_YES if the element is in the set, #GNUNET_NO if not + */ +int +_GSS_is_element_of_operation (struct ElementEntry *ee, + struct Operation *op); + + +#endif diff --git a/src/setu/gnunet-service-setu_protocol.h b/src/setu/gnunet-service-setu_protocol.h new file mode 100644 index 000000000..a2803ee47 --- /dev/null +++ b/src/setu/gnunet-service-setu_protocol.h @@ -0,0 +1,226 @@ +/* + This file is part of GNUnet. + Copyright (C) 2013, 2014 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Florian Dold + * @author Christian Grothoff + * @file set/gnunet-service-set_protocol.h + * @brief Peer-to-Peer messages for gnunet set + */ +#ifndef SET_PROTOCOL_H +#define SET_PROTOCOL_H + +#include "platform.h" +#include "gnunet_common.h" + + +GNUNET_NETWORK_STRUCT_BEGIN + +struct OperationRequestMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST + */ + struct GNUNET_MessageHeader header; + + /** + * Operation to request, values from `enum GNUNET_SET_OperationType` + */ + uint32_t operation GNUNET_PACKED; + + /** + * For Intersection: my element count + */ + uint32_t element_count GNUNET_PACKED; + + /** + * Application-specific identifier of the request. + */ + struct GNUNET_HashCode app_idX; + + /* rest: optional message */ +}; + + +/** + * Message containing buckets of an invertible bloom filter. + * + * If an IBF has too many buckets for an IBF message, + * it is split into multiple messages. + */ +struct IBFMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SET_UNION_P2P_IBF + */ + struct GNUNET_MessageHeader header; + + /** + * Order of the whole ibf, where + * num_buckets = 2^order + */ + uint8_t order; + + /** + * Padding, must be 0. + */ + uint8_t reserved1; + + /** + * Padding, must be 0. + */ + uint16_t reserved2 GNUNET_PACKED; + + /** + * Offset of the strata in the rest of the message + */ + uint32_t offset GNUNET_PACKED; + + /** + * Salt used when hashing elements for this IBF. + */ + uint32_t salt GNUNET_PACKED; + + /* rest: buckets */ +}; + + +struct InquiryMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SET_UNION_P2P_IBF + */ + struct GNUNET_MessageHeader header; + + /** + * Salt used when hashing elements for this inquiry. + */ + uint32_t salt GNUNET_PACKED; + + /** + * Reserved, set to 0. + */ + uint32_t reserved GNUNET_PACKED; + + /* rest: inquiry IBF keys */ +}; + + +/** + * During intersection, the first (and possibly second) message + * send it the number of elements in the set, to allow the peers + * to decide who should start with the Bloom filter. + */ +struct IntersectionElementInfoMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_ELEMENT_INFO + */ + struct GNUNET_MessageHeader header; + + /** + * mutator used with this bloomfilter. + */ + uint32_t sender_element_count GNUNET_PACKED; +}; + + +/** + * Bloom filter messages exchanged for set intersection calculation. + */ +struct BFMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_BF + */ + struct GNUNET_MessageHeader header; + + /** + * Number of elements the sender still has in the set. + */ + uint32_t sender_element_count GNUNET_PACKED; + + /** + * XOR of all hashes over all elements remaining in the set. + * Used to determine termination. + */ + struct GNUNET_HashCode element_xor_hash; + + /** + * Mutator used with this bloomfilter. + */ + uint32_t sender_mutator GNUNET_PACKED; + + /** + * Total length of the bloomfilter data. + */ + uint32_t bloomfilter_total_length GNUNET_PACKED; + + /** + * Number of bits (k-value) used in encoding the bloomfilter. + */ + uint32_t bits_per_element GNUNET_PACKED; + + /** + * rest: the sender's bloomfilter + */ +}; + + +/** + * Last message, send to confirm the final set. Contains the element + * count as it is possible that the peer determined that we were done + * by getting the empty set, which in that case also needs to be + * communicated. + */ +struct IntersectionDoneMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_DONE + */ + struct GNUNET_MessageHeader header; + + /** + * Final number of elements in intersection. + */ + uint32_t final_element_count GNUNET_PACKED; + + /** + * XOR of all hashes over all elements remaining in the set. + */ + struct GNUNET_HashCode element_xor_hash; +}; + + +/** + * Strata estimator together with the peer's overall set size. + */ +struct StrataEstimatorMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SE(C) + */ + struct GNUNET_MessageHeader header; + + uint64_t set_size; +}; + +GNUNET_NETWORK_STRUCT_END + +#endif diff --git a/src/setu/gnunet-service-setu_strata_estimator.c b/src/setu/gnunet-service-setu_strata_estimator.c new file mode 100644 index 000000000..0fa6a6f17 --- /dev/null +++ b/src/setu/gnunet-service-setu_strata_estimator.c @@ -0,0 +1,303 @@ +/* + This file is part of GNUnet + Copyright (C) 2012 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file set/gnunet-service-setu_strata_estimator.c + * @brief invertible bloom filter + * @author Florian Dold + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "ibf.h" +#include "gnunet-service-setu_strata_estimator.h" + + +/** + * Should we try compressing the strata estimator? This will + * break compatibility with the 0.10.1-network. + */ +#define FAIL_10_1_COMPATIBILTIY 1 + + +/** + * Write the given strata estimator to the buffer. + * + * @param se strata estimator to serialize + * @param[out] buf buffer to write to, must be of appropriate size + * @return number of bytes written to @a buf + */ +size_t +strata_estimator_write (const struct StrataEstimator *se, + void *buf) +{ + char *sbuf = buf; + unsigned int i; + size_t osize; + + GNUNET_assert (NULL != se); + for (i = 0; i < se->strata_count; i++) + { + ibf_write_slice (se->strata[i], + 0, + se->ibf_size, + &sbuf[se->ibf_size * IBF_BUCKET_SIZE * i]); + } + osize = se->ibf_size * IBF_BUCKET_SIZE * se->strata_count; +#if FAIL_10_1_COMPATIBILTIY + { + char *cbuf; + size_t nsize; + + if (GNUNET_YES == + GNUNET_try_compression (buf, + osize, + &cbuf, + &nsize)) + { + GNUNET_memcpy (buf, cbuf, nsize); + osize = nsize; + GNUNET_free (cbuf); + } + } +#endif + return osize; +} + + +/** + * Read strata from the buffer into the given strata + * estimator. The strata estimator must already be allocated. + * + * @param buf buffer to read from + * @param buf_len number of bytes in @a buf + * @param is_compressed is the data compressed? + * @param[out] se strata estimator to write to + * @return #GNUNET_OK on success + */ +int +strata_estimator_read (const void *buf, + size_t buf_len, + int is_compressed, + struct StrataEstimator *se) +{ + unsigned int i; + size_t osize; + char *dbuf; + + dbuf = NULL; + if (GNUNET_YES == is_compressed) + { + osize = se->ibf_size * IBF_BUCKET_SIZE * se->strata_count; + dbuf = GNUNET_decompress (buf, + buf_len, + osize); + if (NULL == dbuf) + { + GNUNET_break_op (0); /* bad compressed input data */ + return GNUNET_SYSERR; + } + buf = dbuf; + buf_len = osize; + } + + if (buf_len != se->strata_count * se->ibf_size * IBF_BUCKET_SIZE) + { + GNUNET_break (0); /* very odd error */ + GNUNET_free (dbuf); + return GNUNET_SYSERR; + } + + for (i = 0; i < se->strata_count; i++) + { + ibf_read_slice (buf, 0, se->ibf_size, se->strata[i]); + buf += se->ibf_size * IBF_BUCKET_SIZE; + } + GNUNET_free (dbuf); + return GNUNET_OK; +} + + +/** + * Add a key to the strata estimator. + * + * @param se strata estimator to add the key to + * @param key key to add + */ +void +strata_estimator_insert (struct StrataEstimator *se, + struct IBF_Key key) +{ + uint64_t v; + unsigned int i; + + v = key.key_val; + /* count trailing '1'-bits of v */ + for (i = 0; v & 1; v >>= 1, i++) + /* empty */; + ibf_insert (se->strata[i], key); +} + + +/** + * Remove a key from the strata estimator. + * + * @param se strata estimator to remove the key from + * @param key key to remove + */ +void +strata_estimator_remove (struct StrataEstimator *se, + struct IBF_Key key) +{ + uint64_t v; + unsigned int i; + + v = key.key_val; + /* count trailing '1'-bits of v */ + for (i = 0; v & 1; v >>= 1, i++) + /* empty */; + ibf_remove (se->strata[i], key); +} + + +/** + * Create a new strata estimator with the given parameters. + * + * @param strata_count number of stratas, that is, number of ibfs in the estimator + * @param ibf_size size of each ibf stratum + * @param ibf_hashnum hashnum parameter of each ibf + * @return a freshly allocated, empty strata estimator, NULL on error + */ +struct StrataEstimator * +strata_estimator_create (unsigned int strata_count, + uint32_t ibf_size, + uint8_t ibf_hashnum) +{ + struct StrataEstimator *se; + unsigned int i; + unsigned int j; + + se = GNUNET_new (struct StrataEstimator); + se->strata_count = strata_count; + se->ibf_size = ibf_size; + se->strata = GNUNET_new_array (strata_count, + struct InvertibleBloomFilter *); + for (i = 0; i < strata_count; i++) + { + se->strata[i] = ibf_create (ibf_size, ibf_hashnum); + if (NULL == se->strata[i]) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to allocate memory for strata estimator\n"); + for (j = 0; j < i; j++) + ibf_destroy (se->strata[i]); + GNUNET_free (se); + return NULL; + } + } + return se; +} + + +/** + * Estimate set difference with two strata estimators, + * i.e. arrays of IBFs. + * Does not not modify its arguments. + * + * @param se1 first strata estimator + * @param se2 second strata estimator + * @return the estimated difference + */ +unsigned int +strata_estimator_difference (const struct StrataEstimator *se1, + const struct StrataEstimator *se2) +{ + unsigned int count; + + GNUNET_assert (se1->strata_count == se2->strata_count); + count = 0; + for (int i = se1->strata_count - 1; i >= 0; i--) + { + struct InvertibleBloomFilter *diff; + /* number of keys decoded from the ibf */ + + /* FIXME: implement this without always allocating new IBFs */ + diff = ibf_dup (se1->strata[i]); + ibf_subtract (diff, se2->strata[i]); + for (int ibf_count = 0; GNUNET_YES; ibf_count++) + { + int more; + + more = ibf_decode (diff, NULL, NULL); + if (GNUNET_NO == more) + { + count += ibf_count; + break; + } + /* Estimate if decoding fails or would not terminate */ + if ((GNUNET_SYSERR == more) || (ibf_count > diff->size)) + { + ibf_destroy (diff); + return count * (1 << (i + 1)); + } + } + ibf_destroy (diff); + } + return count; +} + + +/** + * Make a copy of a strata estimator. + * + * @param se the strata estimator to copy + * @return the copy + */ +struct StrataEstimator * +strata_estimator_dup (struct StrataEstimator *se) +{ + struct StrataEstimator *c; + unsigned int i; + + c = GNUNET_new (struct StrataEstimator); + c->strata_count = se->strata_count; + c->ibf_size = se->ibf_size; + c->strata = GNUNET_new_array (se->strata_count, + struct InvertibleBloomFilter *); + for (i = 0; i < se->strata_count; i++) + c->strata[i] = ibf_dup (se->strata[i]); + return c; +} + + +/** + * Destroy a strata estimator, free all of its resources. + * + * @param se strata estimator to destroy. + */ +void +strata_estimator_destroy (struct StrataEstimator *se) +{ + unsigned int i; + + for (i = 0; i < se->strata_count; i++) + ibf_destroy (se->strata[i]); + GNUNET_free (se->strata); + GNUNET_free (se); +} diff --git a/src/setu/gnunet-service-setu_strata_estimator.h b/src/setu/gnunet-service-setu_strata_estimator.h new file mode 100644 index 000000000..afdbcdbbf --- /dev/null +++ b/src/setu/gnunet-service-setu_strata_estimator.h @@ -0,0 +1,169 @@ +/* + This file is part of GNUnet + Copyright (C) 2012 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file set/gnunet-service-setu_strata_estimator.h + * @brief estimator of set difference + * @author Florian Dold + */ + +#ifndef GNUNET_SERVICE_SETU_STRATA_ESTIMATOR_H +#define GNUNET_SERVICE_SETU_STRATA_ESTIMATOR_H + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_util_lib.h" + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + + +/** + * A handle to a strata estimator. + */ +struct StrataEstimator +{ + /** + * The IBFs of this strata estimator. + */ + struct InvertibleBloomFilter **strata; + + /** + * Size of the IBF array in @e strata + */ + unsigned int strata_count; + + /** + * Size of each IBF stratum (in bytes) + */ + unsigned int ibf_size; +}; + + +/** + * Write the given strata estimator to the buffer. + * + * @param se strata estimator to serialize + * @param[out] buf buffer to write to, must be of appropriate size + * @return number of bytes written to @a buf + */ +size_t +strata_estimator_write (const struct StrataEstimator *se, + void *buf); + + +/** + * Read strata from the buffer into the given strata + * estimator. The strata estimator must already be allocated. + * + * @param buf buffer to read from + * @param buf_len number of bytes in @a buf + * @param is_compressed is the data compressed? + * @param[out] se strata estimator to write to + * @return #GNUNET_OK on success + */ +int +strata_estimator_read (const void *buf, + size_t buf_len, + int is_compressed, + struct StrataEstimator *se); + + +/** + * Create a new strata estimator with the given parameters. + * + * @param strata_count number of stratas, that is, number of ibfs in the estimator + * @param ibf_size size of each ibf stratum + * @param ibf_hashnum hashnum parameter of each ibf + * @return a freshly allocated, empty strata estimator, NULL on error + */ +struct StrataEstimator * +strata_estimator_create (unsigned int strata_count, + uint32_t ibf_size, + uint8_t ibf_hashnum); + + +/** + * Get an estimation of the symmetric difference of the elements + * contained in both strata estimators. + * + * @param se1 first strata estimator + * @param se2 second strata estimator + * @return abs(|se1| - |se2|) + */ +unsigned int +strata_estimator_difference (const struct StrataEstimator *se1, + const struct StrataEstimator *se2); + + +/** + * Add a key to the strata estimator. + * + * @param se strata estimator to add the key to + * @param key key to add + */ +void +strata_estimator_insert (struct StrataEstimator *se, + struct IBF_Key key); + + +/** + * Remove a key from the strata estimator. + * + * @param se strata estimator to remove the key from + * @param key key to remove + */ +void +strata_estimator_remove (struct StrataEstimator *se, + struct IBF_Key key); + + +/** + * Destroy a strata estimator, free all of its resources. + * + * @param se strata estimator to destroy. + */ +void +strata_estimator_destroy (struct StrataEstimator *se); + + +/** + * Make a copy of a strata estimator. + * + * @param se the strata estimator to copy + * @return the copy + */ +struct StrataEstimator * +strata_estimator_dup (struct StrataEstimator *se); + + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/setu/gnunet-setu-ibf-profiler.c b/src/setu/gnunet-setu-ibf-profiler.c new file mode 100644 index 000000000..944b63d30 --- /dev/null +++ b/src/setu/gnunet-setu-ibf-profiler.c @@ -0,0 +1,308 @@ +/* + This file is part of GNUnet. + Copyright (C) 2012 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file set/gnunet-set-ibf-profiler.c + * @brief tool for profiling the invertible bloom filter implementation + * @author Florian Dold + */ + +#include "platform.h" +#include "gnunet_util_lib.h" + +#include "ibf.h" + +static unsigned int asize = 10; +static unsigned int bsize = 10; +static unsigned int csize = 10; +static unsigned int hash_num = 4; +static unsigned int ibf_size = 80; + +/* FIXME: add parameter for this */ +static enum GNUNET_CRYPTO_Quality random_quality = GNUNET_CRYPTO_QUALITY_WEAK; + +static struct GNUNET_CONTAINER_MultiHashMap *set_a; +static struct GNUNET_CONTAINER_MultiHashMap *set_b; +/* common elements in a and b */ +static struct GNUNET_CONTAINER_MultiHashMap *set_c; + +static struct GNUNET_CONTAINER_MultiHashMap *key_to_hashcode; + +static struct InvertibleBloomFilter *ibf_a; +static struct InvertibleBloomFilter *ibf_b; + + +static void +register_hashcode (struct GNUNET_HashCode *hash) +{ + struct GNUNET_HashCode replicated; + struct IBF_Key key; + + key = ibf_key_from_hashcode (hash); + ibf_hashcode_from_key (key, &replicated); + (void) GNUNET_CONTAINER_multihashmap_put ( + key_to_hashcode, + &replicated, + GNUNET_memdup (hash, sizeof *hash), + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); +} + + +static void +iter_hashcodes (struct IBF_Key key, + GNUNET_CONTAINER_MulitHashMapIteratorCallback iter, + void *cls) +{ + struct GNUNET_HashCode replicated; + + ibf_hashcode_from_key (key, &replicated); + GNUNET_CONTAINER_multihashmap_get_multiple (key_to_hashcode, + &replicated, + iter, + cls); +} + + +static int +insert_iterator (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct InvertibleBloomFilter *ibf = cls; + + ibf_insert (ibf, ibf_key_from_hashcode (key)); + return GNUNET_YES; +} + + +static int +remove_iterator (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + struct GNUNET_CONTAINER_MultiHashMap *hashmap = cls; + + /* if remove fails, there just was a collision with another key */ + (void) GNUNET_CONTAINER_multihashmap_remove (hashmap, value, NULL); + return GNUNET_YES; +} + + +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_HashCode id; + struct IBF_Key ibf_key; + int i; + int side; + int res; + struct GNUNET_TIME_Absolute start_time; + struct GNUNET_TIME_Relative delta_time; + + set_a = + GNUNET_CONTAINER_multihashmap_create (((asize == 0) ? 1 : (asize + csize)), + GNUNET_NO); + set_b = + GNUNET_CONTAINER_multihashmap_create (((bsize == 0) ? 1 : (bsize + csize)), + GNUNET_NO); + set_c = GNUNET_CONTAINER_multihashmap_create (((csize == 0) ? 1 : csize), + GNUNET_NO); + + key_to_hashcode = + GNUNET_CONTAINER_multihashmap_create (((asize + bsize + csize == 0) + ? 1 + : (asize + bsize + csize)), + GNUNET_NO); + + printf ("hash-num=%u, size=%u, #(A-B)=%u, #(B-A)=%u, #(A&B)=%u\n", + hash_num, + ibf_size, + asize, + bsize, + csize); + + i = 0; + while (i < asize) + { + GNUNET_CRYPTO_hash_create_random (random_quality, &id); + if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (set_a, &id)) + continue; + GNUNET_break (GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put ( + set_a, + &id, + NULL, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + register_hashcode (&id); + i++; + } + i = 0; + while (i < bsize) + { + GNUNET_CRYPTO_hash_create_random (random_quality, &id); + if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (set_a, &id)) + continue; + if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (set_b, &id)) + continue; + GNUNET_break (GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put ( + set_b, + &id, + NULL, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + register_hashcode (&id); + i++; + } + i = 0; + while (i < csize) + { + GNUNET_CRYPTO_hash_create_random (random_quality, &id); + if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (set_a, &id)) + continue; + if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (set_b, &id)) + continue; + if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (set_c, &id)) + continue; + GNUNET_break (GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put ( + set_c, + &id, + NULL, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + register_hashcode (&id); + i++; + } + + ibf_a = ibf_create (ibf_size, hash_num); + ibf_b = ibf_create (ibf_size, hash_num); + if ((NULL == ibf_a) || (NULL == ibf_b)) + { + /* insufficient memory */ + GNUNET_break (0); + GNUNET_SCHEDULER_shutdown (); + return; + } + + + printf ("generated sets\n"); + + start_time = GNUNET_TIME_absolute_get (); + + GNUNET_CONTAINER_multihashmap_iterate (set_a, &insert_iterator, ibf_a); + GNUNET_CONTAINER_multihashmap_iterate (set_b, &insert_iterator, ibf_b); + GNUNET_CONTAINER_multihashmap_iterate (set_c, &insert_iterator, ibf_a); + GNUNET_CONTAINER_multihashmap_iterate (set_c, &insert_iterator, ibf_b); + + delta_time = GNUNET_TIME_absolute_get_duration (start_time); + + printf ("encoded in: %s\n", + GNUNET_STRINGS_relative_time_to_string (delta_time, GNUNET_NO)); + + ibf_subtract (ibf_a, ibf_b); + + + start_time = GNUNET_TIME_absolute_get (); + + for (i = 0; i <= asize + bsize; i++) + { + res = ibf_decode (ibf_a, &side, &ibf_key); + if (GNUNET_SYSERR == res) + { + printf ("decode failed, %u/%u elements left\n", + GNUNET_CONTAINER_multihashmap_size (set_a) + + GNUNET_CONTAINER_multihashmap_size (set_b), + asize + bsize); + return; + } + if (GNUNET_NO == res) + { + if ((0 == GNUNET_CONTAINER_multihashmap_size (set_b)) && + (0 == GNUNET_CONTAINER_multihashmap_size (set_a))) + { + delta_time = GNUNET_TIME_absolute_get_duration (start_time); + printf ("decoded successfully in: %s\n", + GNUNET_STRINGS_relative_time_to_string (delta_time, GNUNET_NO)); + } + else + { + printf ("decode missed elements (should never happen)\n"); + } + return; + } + + if (side == 1) + iter_hashcodes (ibf_key, remove_iterator, set_a); + if (side == -1) + iter_hashcodes (ibf_key, remove_iterator, set_b); + } + printf ("cyclic IBF, %u/%u elements left\n", + GNUNET_CONTAINER_multihashmap_size (set_a) + + GNUNET_CONTAINER_multihashmap_size (set_b), + asize + bsize); +} + + +int +main (int argc, char **argv) +{ + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_option_uint ('A', + "asize", + NULL, + gettext_noop ("number of element in set A-B"), + &asize), + + GNUNET_GETOPT_option_uint ('B', + "bsize", + NULL, + gettext_noop ("number of element in set B-A"), + &bsize), + + GNUNET_GETOPT_option_uint ('C', + "csize", + NULL, + gettext_noop ( + "number of common elements in A and B"), + &csize), + + GNUNET_GETOPT_option_uint ('k', + "hash-num", + NULL, + gettext_noop ("hash num"), + &hash_num), + + GNUNET_GETOPT_option_uint ('s', + "ibf-size", + NULL, + gettext_noop ("ibf size"), + &ibf_size), + + GNUNET_GETOPT_OPTION_END + }; + + GNUNET_PROGRAM_run2 (argc, + argv, + "gnunet-consensus-ibf", + "help", + options, + &run, + NULL, + GNUNET_YES); + return 0; +} diff --git a/src/setu/gnunet-setu-profiler.c b/src/setu/gnunet-setu-profiler.c new file mode 100644 index 000000000..8d6a2dc8c --- /dev/null +++ b/src/setu/gnunet-setu-profiler.c @@ -0,0 +1,499 @@ +/* + This file is part of GNUnet + Copyright (C) 2013 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file setu/gnunet-setu-profiler.c + * @brief profiling tool for set + * @author Florian Dold + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_statistics_service.h" +#include "gnunet_setu_service.h" +#include "gnunet_testbed_service.h" + + +static int ret; + +static unsigned int num_a = 5; +static unsigned int num_b = 5; +static unsigned int num_c = 20; + +static char *op_str = "union"; + +const static struct GNUNET_CONFIGURATION_Handle *config; + +struct SetInfo +{ + char *id; + struct GNUNET_SETU_Handle *set; + struct GNUNET_SETU_OperationHandle *oh; + struct GNUNET_CONTAINER_MultiHashMap *sent; + struct GNUNET_CONTAINER_MultiHashMap *received; + int done; +} info1, info2; + +static struct GNUNET_CONTAINER_MultiHashMap *common_sent; + +static struct GNUNET_HashCode app_id; + +static struct GNUNET_PeerIdentity local_peer; + +static struct GNUNET_SETU_ListenHandle *set_listener; + +static int byzantine; +static unsigned int force_delta; +static unsigned int force_full; +static unsigned int element_size = 32; + +/** + * Handle to the statistics service. + */ +static struct GNUNET_STATISTICS_Handle *statistics; + +/** + * The profiler will write statistics + * for all peers to the file with this name. + */ +static char *statistics_filename; + +/** + * The profiler will write statistics + * for all peers to this file. + */ +static FILE *statistics_file; + + +static int +map_remove_iterator (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct GNUNET_CONTAINER_MultiHashMap *m = cls; + int ret; + + GNUNET_assert (NULL != key); + + ret = GNUNET_CONTAINER_multihashmap_remove_all (m, key); + if (GNUNET_OK != ret) + printf ("spurious element\n"); + return GNUNET_YES; +} + + +/** + * Callback function to process statistic values. + * + * @param cls closure + * @param subsystem name of subsystem that created the statistic + * @param name the name of the datum + * @param value the current value + * @param is_persistent #GNUNET_YES if the value is persistent, #GNUNET_NO if not + * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration + */ +static int +statistics_result (void *cls, + const char *subsystem, + const char *name, + uint64_t value, + int is_persistent) +{ + if (NULL != statistics_file) + { + fprintf (statistics_file, "%s\t%s\t%lu\n", subsystem, name, (unsigned + long) value); + } + return GNUNET_OK; +} + + +static void +statistics_done (void *cls, + int success) +{ + GNUNET_assert (GNUNET_YES == success); + if (NULL != statistics_file) + fclose (statistics_file); + GNUNET_SCHEDULER_shutdown (); +} + + +static void +check_all_done (void) +{ + if ((info1.done == GNUNET_NO) || (info2.done == GNUNET_NO)) + return; + + GNUNET_CONTAINER_multihashmap_iterate (info1.received, map_remove_iterator, + info2.sent); + GNUNET_CONTAINER_multihashmap_iterate (info2.received, map_remove_iterator, + info1.sent); + + printf ("set a: %d missing elements\n", GNUNET_CONTAINER_multihashmap_size ( + info1.sent)); + printf ("set b: %d missing elements\n", GNUNET_CONTAINER_multihashmap_size ( + info2.sent)); + + if (NULL == statistics_filename) + { + GNUNET_SCHEDULER_shutdown (); + return; + } + + statistics_file = fopen (statistics_filename, "w"); + GNUNET_STATISTICS_get (statistics, NULL, NULL, + &statistics_done, + &statistics_result, NULL); +} + + +static void +set_result_cb (void *cls, + const struct GNUNET_SETU_Element *element, + uint64_t current_size, + enum GNUNET_SETU_Status status) +{ + struct SetInfo *info = cls; + + GNUNET_assert (GNUNET_NO == info->done); + switch (status) + { + case GNUNET_SETU_STATUS_DONE: + info->done = GNUNET_YES; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "set %s done\n", info->id); + check_all_done (); + info->oh = NULL; + return; + + case GNUNET_SETU_STATUS_FAILURE: + info->oh = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "failure\n"); + GNUNET_SCHEDULER_shutdown (); + return; + + case GNUNET_SETU_STATUS_ADD_LOCAL: + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "set %s: local element\n", info->id); + break; + default: + GNUNET_assert (0); + } + + if (element->size != element_size) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "wrong element size: %u, expected %u\n", + element->size, + (unsigned int) sizeof(struct GNUNET_HashCode)); + GNUNET_assert (0); + } + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "set %s: got element (%s)\n", + info->id, GNUNET_h2s (element->data)); + GNUNET_assert (NULL != element->data); + struct GNUNET_HashCode data_hash; + GNUNET_CRYPTO_hash (element->data, element_size, &data_hash); + GNUNET_CONTAINER_multihashmap_put (info->received, + &data_hash, NULL, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE); +} + + +static void +set_listen_cb (void *cls, + const struct GNUNET_PeerIdentity *other_peer, + const struct GNUNET_MessageHeader *context_msg, + struct GNUNET_SETU_Request *request) +{ + /* max. 2 options plus terminator */ + struct GNUNET_SETU_Option opts[3] = { { 0 } }; + unsigned int n_opts = 0; + + if (NULL == request) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "listener failed\n"); + return; + } + GNUNET_assert (NULL == info2.oh); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "set listen cb called\n"); + if (byzantine) + { + opts[n_opts++] = (struct GNUNET_SETU_Option) { .type = + GNUNET_SETU_OPTION_BYZANTINE }; + } + GNUNET_assert (! (force_full && force_delta)); + if (force_full) + { + opts[n_opts++] = (struct GNUNET_SETU_Option) { .type = + GNUNET_SETU_OPTION_FORCE_FULL }; + } + if (force_delta) + { + opts[n_opts++] = (struct GNUNET_SETU_Option) { .type = + GNUNET_SETU_OPTION_FORCE_DELTA }; + } + + opts[n_opts].type = 0; + info2.oh = GNUNET_SETU_accept (request, + opts, + set_result_cb, &info2); + GNUNET_SETU_commit (info2.oh, info2.set); +} + + +static int +set_insert_iterator (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct GNUNET_SETU_Handle *set = cls; + struct GNUNET_SETU_Element el; + + el.element_type = 0; + el.data = value; + el.size = element_size; + GNUNET_SETU_add_element (set, &el, NULL, NULL); + return GNUNET_YES; +} + + +static void +handle_shutdown (void *cls) +{ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Shutting down set profiler\n"); + if (NULL != set_listener) + { + GNUNET_SETU_listen_cancel (set_listener); + set_listener = NULL; + } + if (NULL != info1.oh) + { + GNUNET_SETU_operation_cancel (info1.oh); + info1.oh = NULL; + } + if (NULL != info2.oh) + { + GNUNET_SETU_operation_cancel (info2.oh); + info2.oh = NULL; + } + if (NULL != info1.set) + { + GNUNET_SETU_destroy (info1.set); + info1.set = NULL; + } + if (NULL != info2.set) + { + GNUNET_SETU_destroy (info2.set); + info2.set = NULL; + } + GNUNET_STATISTICS_destroy (statistics, GNUNET_NO); +} + + +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + unsigned int i; + struct GNUNET_HashCode hash; + /* max. 2 options plus terminator */ + struct GNUNET_SETU_Option opts[3] = { { 0 } }; + unsigned int n_opts = 0; + + config = cfg; + + GNUNET_assert (element_size > 0); + + if (GNUNET_OK != GNUNET_CRYPTO_get_peer_identity (cfg, &local_peer)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "could not retrieve host identity\n"); + ret = 0; + return; + } + + statistics = GNUNET_STATISTICS_create ("set-profiler", cfg); + + GNUNET_SCHEDULER_add_shutdown (&handle_shutdown, NULL); + + info1.id = "a"; + info2.id = "b"; + + info1.sent = GNUNET_CONTAINER_multihashmap_create (num_a + 1, GNUNET_NO); + info2.sent = GNUNET_CONTAINER_multihashmap_create (num_b + 1, GNUNET_NO); + common_sent = GNUNET_CONTAINER_multihashmap_create (num_c + 1, GNUNET_NO); + + info1.received = GNUNET_CONTAINER_multihashmap_create (num_a + 1, GNUNET_NO); + info2.received = GNUNET_CONTAINER_multihashmap_create (num_b + 1, GNUNET_NO); + + for (i = 0; i < num_a; i++) + { + char *data = GNUNET_malloc (element_size); + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, data, element_size); + GNUNET_CRYPTO_hash (data, element_size, &hash); + GNUNET_CONTAINER_multihashmap_put (info1.sent, &hash, data, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE); + } + + for (i = 0; i < num_b; i++) + { + char *data = GNUNET_malloc (element_size); + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, data, element_size); + GNUNET_CRYPTO_hash (data, element_size, &hash); + GNUNET_CONTAINER_multihashmap_put (info2.sent, &hash, data, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE); + } + + for (i = 0; i < num_c; i++) + { + char *data = GNUNET_malloc (element_size); + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, data, element_size); + GNUNET_CRYPTO_hash (data, element_size, &hash); + GNUNET_CONTAINER_multihashmap_put (common_sent, &hash, data, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE); + } + + GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_STRONG, &app_id); + + /* FIXME: also implement intersection etc. */ + info1.set = GNUNET_SETU_create (config); + info2.set = GNUNET_SETU_create (config); + + GNUNET_CONTAINER_multihashmap_iterate (info1.sent, set_insert_iterator, + info1.set); + GNUNET_CONTAINER_multihashmap_iterate (info2.sent, set_insert_iterator, + info2.set); + GNUNET_CONTAINER_multihashmap_iterate (common_sent, set_insert_iterator, + info1.set); + GNUNET_CONTAINER_multihashmap_iterate (common_sent, set_insert_iterator, + info2.set); + + set_listener = GNUNET_SETU_listen (config, + &app_id, + &set_listen_cb, + NULL); + + + if (byzantine) + { + opts[n_opts++] = (struct GNUNET_SETU_Option) { .type = + GNUNET_SETU_OPTION_BYZANTINE }; + } + GNUNET_assert (! (force_full && force_delta)); + if (force_full) + { + opts[n_opts++] = (struct GNUNET_SETU_Option) { .type = + GNUNET_SETU_OPTION_FORCE_FULL }; + } + if (force_delta) + { + opts[n_opts++] = (struct GNUNET_SETU_Option) { .type = + GNUNET_SETU_OPTION_FORCE_DELTA }; + } + + opts[n_opts].type = 0; + + info1.oh = GNUNET_SETU_prepare (&local_peer, &app_id, NULL, + opts, + set_result_cb, &info1); + GNUNET_SETU_commit (info1.oh, info1.set); + GNUNET_SETU_destroy (info1.set); + info1.set = NULL; +} + + +static void +pre_run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + if (0 != GNUNET_TESTING_peer_run ("set-profiler", + cfgfile, + &run, NULL)) + ret = 2; +} + + +int +main (int argc, char **argv) +{ + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_option_uint ('A', + "num-first", + NULL, + gettext_noop ("number of values"), + &num_a), + + GNUNET_GETOPT_option_uint ('B', + "num-second", + NULL, + gettext_noop ("number of values"), + &num_b), + + GNUNET_GETOPT_option_flag ('b', + "byzantine", + gettext_noop ("use byzantine mode"), + &byzantine), + + GNUNET_GETOPT_option_uint ('f', + "force-full", + NULL, + gettext_noop ("force sending full set"), + &force_full), + + GNUNET_GETOPT_option_uint ('d', + "force-delta", + NULL, + gettext_noop ("number delta operation"), + &force_delta), + + GNUNET_GETOPT_option_uint ('C', + "num-common", + NULL, + gettext_noop ("number of values"), + &num_c), + + GNUNET_GETOPT_option_string ('x', + "operation", + NULL, + gettext_noop ("operation to execute"), + &op_str), + + GNUNET_GETOPT_option_uint ('w', + "element-size", + NULL, + gettext_noop ("element size"), + &element_size), + + GNUNET_GETOPT_option_filename ('s', + "statistics", + "FILENAME", + gettext_noop ("write statistics to file"), + &statistics_filename), + + GNUNET_GETOPT_OPTION_END + }; + + GNUNET_PROGRAM_run2 (argc, argv, "gnunet-set-profiler", + "help", + options, &pre_run, NULL, GNUNET_YES); + return ret; +} diff --git a/src/setu/ibf.c b/src/setu/ibf.c new file mode 100644 index 000000000..1532afceb --- /dev/null +++ b/src/setu/ibf.c @@ -0,0 +1,409 @@ +/* + This file is part of GNUnet + Copyright (C) 2012 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file set/ibf.c + * @brief implementation of the invertible bloom filter + * @author Florian Dold + */ + +#include "ibf.h" + +/** + * Compute the key's hash from the key. + * Redefine to use a different hash function. + */ +#define IBF_KEY_HASH_VAL(k) (GNUNET_CRYPTO_crc32_n (&(k), sizeof(struct \ + IBF_KeyHash))) + +/** + * Create a key from a hashcode. + * + * @param hash the hashcode + * @return a key + */ +struct IBF_Key +ibf_key_from_hashcode (const struct GNUNET_HashCode *hash) +{ + return *(struct IBF_Key *) hash; +} + + +/** + * Create a hashcode from a key, by replicating the key + * until the hascode is filled + * + * @param key the key + * @param dst hashcode to store the result in + */ +void +ibf_hashcode_from_key (struct IBF_Key key, + struct GNUNET_HashCode *dst) +{ + struct IBF_Key *p; + unsigned int i; + const unsigned int keys_per_hashcode = sizeof(struct GNUNET_HashCode) + / sizeof(struct IBF_Key); + + p = (struct IBF_Key *) dst; + for (i = 0; i < keys_per_hashcode; i++) + *p++ = key; +} + + +/** + * Create an invertible bloom filter. + * + * @param size number of IBF buckets + * @param hash_num number of buckets one element is hashed in + * @return the newly created invertible bloom filter, NULL on error + */ +struct InvertibleBloomFilter * +ibf_create (uint32_t size, uint8_t hash_num) +{ + struct InvertibleBloomFilter *ibf; + + GNUNET_assert (0 != size); + + ibf = GNUNET_new (struct InvertibleBloomFilter); + ibf->count = GNUNET_malloc_large (size * sizeof(uint8_t)); + if (NULL == ibf->count) + { + GNUNET_free (ibf); + return NULL; + } + ibf->key_sum = GNUNET_malloc_large (size * sizeof(struct IBF_Key)); + if (NULL == ibf->key_sum) + { + GNUNET_free (ibf->count); + GNUNET_free (ibf); + return NULL; + } + ibf->key_hash_sum = GNUNET_malloc_large (size * sizeof(struct IBF_KeyHash)); + if (NULL == ibf->key_hash_sum) + { + GNUNET_free (ibf->key_sum); + GNUNET_free (ibf->count); + GNUNET_free (ibf); + return NULL; + } + ibf->size = size; + ibf->hash_num = hash_num; + + return ibf; +} + + +/** + * Store unique bucket indices for the specified key in dst. + */ +static void +ibf_get_indices (const struct InvertibleBloomFilter *ibf, + struct IBF_Key key, + int *dst) +{ + uint32_t filled; + uint32_t i; + uint32_t bucket; + + bucket = GNUNET_CRYPTO_crc32_n (&key, sizeof key); + for (i = 0, filled = 0; filled < ibf->hash_num; i++) + { + unsigned int j; + uint64_t x; + for (j = 0; j < filled; j++) + if (dst[j] == bucket) + goto try_next; + dst[filled++] = bucket % ibf->size; +try_next:; + x = ((uint64_t) bucket << 32) | i; + bucket = GNUNET_CRYPTO_crc32_n (&x, sizeof x); + } +} + + +static void +ibf_insert_into (struct InvertibleBloomFilter *ibf, + struct IBF_Key key, + const int *buckets, int side) +{ + int i; + + for (i = 0; i < ibf->hash_num; i++) + { + const int bucket = buckets[i]; + ibf->count[bucket].count_val += side; + ibf->key_sum[bucket].key_val ^= key.key_val; + ibf->key_hash_sum[bucket].key_hash_val + ^= IBF_KEY_HASH_VAL (key); + } +} + + +/** + * Insert a key into an IBF. + * + * @param ibf the IBF + * @param key the element's hash code + */ +void +ibf_insert (struct InvertibleBloomFilter *ibf, struct IBF_Key key) +{ + int buckets[ibf->hash_num]; + + GNUNET_assert (ibf->hash_num <= ibf->size); + ibf_get_indices (ibf, key, buckets); + ibf_insert_into (ibf, key, buckets, 1); +} + + +/** + * Remove a key from an IBF. + * + * @param ibf the IBF + * @param key the element's hash code + */ +void +ibf_remove (struct InvertibleBloomFilter *ibf, struct IBF_Key key) +{ + int buckets[ibf->hash_num]; + + GNUNET_assert (ibf->hash_num <= ibf->size); + ibf_get_indices (ibf, key, buckets); + ibf_insert_into (ibf, key, buckets, -1); +} + + +/** + * Test is the IBF is empty, i.e. all counts, keys and key hashes are zero. + */ +static int +ibf_is_empty (struct InvertibleBloomFilter *ibf) +{ + int i; + + for (i = 0; i < ibf->size; i++) + { + if (0 != ibf->count[i].count_val) + return GNUNET_NO; + if (0 != ibf->key_hash_sum[i].key_hash_val) + return GNUNET_NO; + if (0 != ibf->key_sum[i].key_val) + return GNUNET_NO; + } + return GNUNET_YES; +} + + +/** + * Decode and remove an element from the IBF, if possible. + * + * @param ibf the invertible bloom filter to decode + * @param ret_side sign of the cell's count where the decoded element came from. + * A negative sign indicates that the element was recovered + * resides in an IBF that was previously subtracted from. + * @param ret_id receives the hash code of the decoded element, if successful + * @return GNUNET_YES if decoding an element was successful, + * GNUNET_NO if the IBF is empty, + * GNUNET_SYSERR if the decoding has failed + */ +int +ibf_decode (struct InvertibleBloomFilter *ibf, + int *ret_side, struct IBF_Key *ret_id) +{ + struct IBF_KeyHash hash; + int i; + int buckets[ibf->hash_num]; + + GNUNET_assert (NULL != ibf); + + for (i = 0; i < ibf->size; i++) + { + int j; + int hit; + + /* we can only decode from pure buckets */ + if ((1 != ibf->count[i].count_val) && (-1 != ibf->count[i].count_val)) + continue; + + hash.key_hash_val = IBF_KEY_HASH_VAL (ibf->key_sum[i]); + + /* test if the hash matches the key */ + if (hash.key_hash_val != ibf->key_hash_sum[i].key_hash_val) + continue; + + /* test if key in bucket hits its own location, + * if not, the key hash was subject to collision */ + hit = GNUNET_NO; + ibf_get_indices (ibf, ibf->key_sum[i], buckets); + for (j = 0; j < ibf->hash_num; j++) + if (buckets[j] == i) + hit = GNUNET_YES; + + if (GNUNET_NO == hit) + continue; + + if (NULL != ret_side) + *ret_side = ibf->count[i].count_val; + if (NULL != ret_id) + *ret_id = ibf->key_sum[i]; + + /* insert on the opposite side, effectively removing the element */ + ibf_insert_into (ibf, ibf->key_sum[i], buckets, -ibf->count[i].count_val); + + return GNUNET_YES; + } + + if (GNUNET_YES == ibf_is_empty (ibf)) + return GNUNET_NO; + return GNUNET_SYSERR; +} + + +/** + * Write buckets from an ibf to a buffer. + * Exactly (IBF_BUCKET_SIZE*ibf->size) bytes are written to buf. + * + * @param ibf the ibf to write + * @param start with which bucket to start + * @param count how many buckets to write + * @param buf buffer to write the data to + */ +void +ibf_write_slice (const struct InvertibleBloomFilter *ibf, uint32_t start, + uint32_t count, void *buf) +{ + struct IBF_Key *key_dst; + struct IBF_KeyHash *key_hash_dst; + struct IBF_Count *count_dst; + + GNUNET_assert (start + count <= ibf->size); + + /* copy keys */ + key_dst = (struct IBF_Key *) buf; + GNUNET_memcpy (key_dst, ibf->key_sum + start, count * sizeof *key_dst); + key_dst += count; + /* copy key hashes */ + key_hash_dst = (struct IBF_KeyHash *) key_dst; + GNUNET_memcpy (key_hash_dst, ibf->key_hash_sum + start, count + * sizeof *key_hash_dst); + key_hash_dst += count; + /* copy counts */ + count_dst = (struct IBF_Count *) key_hash_dst; + GNUNET_memcpy (count_dst, ibf->count + start, count * sizeof *count_dst); +} + + +/** + * Read buckets from a buffer into an ibf. + * + * @param buf pointer to the buffer to read from + * @param start which bucket to start at + * @param count how many buckets to read + * @param ibf the ibf to read from + */ +void +ibf_read_slice (const void *buf, uint32_t start, uint32_t count, struct + InvertibleBloomFilter *ibf) +{ + struct IBF_Key *key_src; + struct IBF_KeyHash *key_hash_src; + struct IBF_Count *count_src; + + GNUNET_assert (count > 0); + GNUNET_assert (start + count <= ibf->size); + + /* copy keys */ + key_src = (struct IBF_Key *) buf; + GNUNET_memcpy (ibf->key_sum + start, key_src, count * sizeof *key_src); + key_src += count; + /* copy key hashes */ + key_hash_src = (struct IBF_KeyHash *) key_src; + GNUNET_memcpy (ibf->key_hash_sum + start, key_hash_src, count + * sizeof *key_hash_src); + key_hash_src += count; + /* copy counts */ + count_src = (struct IBF_Count *) key_hash_src; + GNUNET_memcpy (ibf->count + start, count_src, count * sizeof *count_src); +} + + +/** + * Subtract ibf2 from ibf1, storing the result in ibf1. + * The two IBF's must have the same parameters size and hash_num. + * + * @param ibf1 IBF that is subtracted from + * @param ibf2 IBF that will be subtracted from ibf1 + */ +void +ibf_subtract (struct InvertibleBloomFilter *ibf1, const struct + InvertibleBloomFilter *ibf2) +{ + int i; + + GNUNET_assert (ibf1->size == ibf2->size); + GNUNET_assert (ibf1->hash_num == ibf2->hash_num); + + for (i = 0; i < ibf1->size; i++) + { + ibf1->count[i].count_val -= ibf2->count[i].count_val; + ibf1->key_hash_sum[i].key_hash_val ^= ibf2->key_hash_sum[i].key_hash_val; + ibf1->key_sum[i].key_val ^= ibf2->key_sum[i].key_val; + } +} + + +/** + * Create a copy of an IBF, the copy has to be destroyed properly. + * + * @param ibf the IBF to copy + */ +struct InvertibleBloomFilter * +ibf_dup (const struct InvertibleBloomFilter *ibf) +{ + struct InvertibleBloomFilter *copy; + + copy = GNUNET_malloc (sizeof *copy); + copy->hash_num = ibf->hash_num; + copy->size = ibf->size; + copy->key_hash_sum = GNUNET_memdup (ibf->key_hash_sum, ibf->size + * sizeof(struct IBF_KeyHash)); + copy->key_sum = GNUNET_memdup (ibf->key_sum, ibf->size * sizeof(struct + IBF_Key)); + copy->count = GNUNET_memdup (ibf->count, ibf->size * sizeof(struct + IBF_Count)); + return copy; +} + + +/** + * Destroy all resources associated with the invertible bloom filter. + * No more ibf_*-functions may be called on ibf after calling destroy. + * + * @param ibf the intertible bloom filter to destroy + */ +void +ibf_destroy (struct InvertibleBloomFilter *ibf) +{ + GNUNET_free (ibf->key_sum); + GNUNET_free (ibf->key_hash_sum); + GNUNET_free (ibf->count); + GNUNET_free (ibf); +} diff --git a/src/setu/ibf.h b/src/setu/ibf.h new file mode 100644 index 000000000..7c2ab33b1 --- /dev/null +++ b/src/setu/ibf.h @@ -0,0 +1,255 @@ +/* + This file is part of GNUnet + Copyright (C) 2012 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file set/ibf.h + * @brief invertible bloom filter + * @author Florian Dold + */ + +#ifndef GNUNET_CONSENSUS_IBF_H +#define GNUNET_CONSENSUS_IBF_H + +#include "platform.h" +#include "gnunet_util_lib.h" + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + + +/** + * Keys that can be inserted into and removed from an IBF. + */ +struct IBF_Key +{ + uint64_t key_val; +}; + + +/** + * Hash of an IBF key. + */ +struct IBF_KeyHash +{ + uint32_t key_hash_val; +}; + + +/** + * Type of the count field of IBF buckets. + */ +struct IBF_Count +{ + int8_t count_val; +}; + + +/** + * Size of one ibf bucket in bytes + */ +#define IBF_BUCKET_SIZE (sizeof(struct IBF_Count) + sizeof(struct IBF_Key) \ + + sizeof(struct IBF_KeyHash)) + + +/** + * Invertible bloom filter (IBF). + * + * An IBF is a counting bloom filter that has the ability to restore + * the hashes of its stored elements with high probability. + */ +struct InvertibleBloomFilter +{ + /** + * How many cells does this IBF have? + */ + uint32_t size; + + /** + * In how many cells do we hash one element? + * Usually 4 or 3. + */ + uint8_t hash_num; + + /** + * Xor sums of the elements' keys, used to identify the elements. + * Array of 'size' elements. + */ + struct IBF_Key *key_sum; + + /** + * Xor sums of the hashes of the keys of inserted elements. + * Array of 'size' elements. + */ + struct IBF_KeyHash *key_hash_sum; + + /** + * How many times has a bucket been hit? + * Can be negative, as a result of IBF subtraction. + * Array of 'size' elements. + */ + struct IBF_Count *count; +}; + + +/** + * Write buckets from an ibf to a buffer. + * Exactly (IBF_BUCKET_SIZE*ibf->size) bytes are written to buf. + * + * @param ibf the ibf to write + * @param start with which bucket to start + * @param count how many buckets to write + * @param buf buffer to write the data to + */ +void +ibf_write_slice (const struct InvertibleBloomFilter *ibf, + uint32_t start, + uint32_t count, + void *buf); + + +/** + * Read buckets from a buffer into an ibf. + * + * @param buf pointer to the buffer to read from + * @param start which bucket to start at + * @param count how many buckets to read + * @param ibf the ibf to write to + */ +void +ibf_read_slice (const void *buf, + uint32_t start, + uint32_t count, + struct InvertibleBloomFilter *ibf); + + +/** + * Create a key from a hashcode. + * + * @param hash the hashcode + * @return a key + */ +struct IBF_Key +ibf_key_from_hashcode (const struct GNUNET_HashCode *hash); + + +/** + * Create a hashcode from a key, by replicating the key + * until the hascode is filled + * + * @param key the key + * @param dst hashcode to store the result in + */ +void +ibf_hashcode_from_key (struct IBF_Key key, struct GNUNET_HashCode *dst); + + +/** + * Create an invertible bloom filter. + * + * @param size number of IBF buckets + * @param hash_num number of buckets one element is hashed in, usually 3 or 4 + * @return the newly created invertible bloom filter, NULL on error + */ +struct InvertibleBloomFilter * +ibf_create (uint32_t size, uint8_t hash_num); + + +/** + * Insert a key into an IBF. + * + * @param ibf the IBF + * @param key the element's hash code + */ +void +ibf_insert (struct InvertibleBloomFilter *ibf, struct IBF_Key key); + + +/** + * Remove a key from an IBF. + * + * @param ibf the IBF + * @param key the element's hash code + */ +void +ibf_remove (struct InvertibleBloomFilter *ibf, struct IBF_Key key); + + +/** + * Subtract ibf2 from ibf1, storing the result in ibf1. + * The two IBF's must have the same parameters size and hash_num. + * + * @param ibf1 IBF that is subtracted from + * @param ibf2 IBF that will be subtracted from ibf1 + */ +void +ibf_subtract (struct InvertibleBloomFilter *ibf1, + const struct InvertibleBloomFilter *ibf2); + + +/** + * Decode and remove an element from the IBF, if possible. + * + * @param ibf the invertible bloom filter to decode + * @param ret_side sign of the cell's count where the decoded element came from. + * A negative sign indicates that the element was recovered + * resides in an IBF that was previously subtracted from. + * @param ret_id receives the hash code of the decoded element, if successful + * @return #GNUNET_YES if decoding an element was successful, + * #GNUNET_NO if the IBF is empty, + * #GNUNET_SYSERR if the decoding has failed + */ +int +ibf_decode (struct InvertibleBloomFilter *ibf, + int *ret_side, + struct IBF_Key *ret_id); + + +/** + * Create a copy of an IBF, the copy has to be destroyed properly. + * + * @param ibf the IBF to copy + */ +struct InvertibleBloomFilter * +ibf_dup (const struct InvertibleBloomFilter *ibf); + + +/** + * Destroy all resources associated with the invertible bloom filter. + * No more ibf_*-functions may be called on ibf after calling destroy. + * + * @param ibf the intertible bloom filter to destroy + */ +void +ibf_destroy (struct InvertibleBloomFilter *ibf); + + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/setu/ibf_sim.c b/src/setu/ibf_sim.c new file mode 100644 index 000000000..6415d00e1 --- /dev/null +++ b/src/setu/ibf_sim.c @@ -0,0 +1,142 @@ +/* + This file is part of GNUnet + Copyright (C) 2013 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file set/ibf_sim.c + * @brief implementation of simulation for invertible bloom filter + * @author Florian Dold + * + * This code was used for some internal experiments, it is not + * build or shipped as part of the GNUnet system. + */ +#include +#include +#include + +#define MAX_IBF_DECODE 16 + +/* report average over how many rounds? */ +#define ROUNDS 100000 + +/* enable one of the three below */ +// simple fix +#define FIX1 0 +// possibly slightly better fix for large IBF_DECODE values +#define FIX2 1 + +// SIGCOMM algorithm +#define STRATA 0 + +// print each value? +#define VERBOSE 0 +// avoid assembly? (ASM is about 50% faster) +#define SLOW 0 + +int +main (int argc, char **argv) +{ + unsigned int round; + unsigned int buckets[31]; // max is 2^31 as 'random' returns only between 0 and 2^31 + unsigned int i; + int j; + unsigned int r; + unsigned int ret; + unsigned long long total; + unsigned int want; + double predict; + + srandom (time (NULL)); + total = 0; + want = atoi (argv[1]); + for (round = 0; round < ROUNDS; round++) + { + memset (buckets, 0, sizeof(buckets)); + for (i = 0; i < want; i++) + { + /* FIXME: might want to use 'better' PRNG to avoid + PRNG-induced biases */ + r = random (); + if (0 == r) + continue; +#if SLOW + for (j = 0; (j < 31) && (0 == (r & (1 << j))); j++) + ; +#else + /* use assembly / gcc */ + j = __builtin_ffs (r) - 1; +#endif + buckets[j]++; + } + ret = 0; + predict = 0.0; + for (j = 31; j >= 0; j--) + { +#if FIX1 + /* improved algorithm, for 1000 elements with IBF-DECODE 8, I + get 990/1000 elements on average over 1 million runs; key + idea being to stop short of the 'last' possible IBF as + otherwise a "lowball" per-chance would unduely influence the + result */if ((j > 0) && + (buckets[j - 1] > MAX_IBF_DECODE)) + { + ret *= (1 << (j + 1)); + break; + } +#endif +#if FIX2 + /* another improvement: don't just always cut off the last one, + but rather try to predict based on all previous values where + that "last" one is; additional prediction can only really + work if MAX_IBF_DECODE is sufficiently high */ + if ((j > 0) && + ((buckets[j - 1] > MAX_IBF_DECODE) || + (predict > MAX_IBF_DECODE))) + { + ret *= (1 << (j + 1)); + break; + } +#endif +#if STRATA + /* original algorithm, for 1000 elements with IBF-DECODE 8, + I get 920/1000 elements on average over 1 million runs */ + if (buckets[j] > MAX_IBF_DECODE) + { + ret *= (1 << (j + 1)); + break; + } +#endif + ret += buckets[j]; + predict = (buckets[j] + 2.0 * predict) / 2.0; + } +#if VERBOSE + fprintf (stderr, "%u ", ret); +#endif + total += ret; + } + fprintf (stderr, "\n"); + fprintf (stdout, "average %llu\n", total / ROUNDS); + return 0; +} + + +/* TODO: should calculate stddev of the results to also be able to + say something about the stability of the results, outside of + large-scale averages -- gaining 8% precision at the expense of + 50% additional variance might not be worth it... */ diff --git a/src/setu/plugin_block_setu_test.c b/src/setu/plugin_block_setu_test.c new file mode 100644 index 000000000..1de086092 --- /dev/null +++ b/src/setu/plugin_block_setu_test.c @@ -0,0 +1,123 @@ +/* + This file is part of GNUnet + Copyright (C) 2017 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file set/plugin_block_set_test.c + * @brief set test block, recognizes elements with non-zero first byte as invalid + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_block_plugin.h" +#include "gnunet_block_group_lib.h" + + +/** + * Function called to validate a reply or a request. For + * request evaluation, simply pass "NULL" for the reply_block. + * + * @param cls closure + * @param ctx block context + * @param type block type + * @param group block group to use + * @param eo control flags + * @param query original query (hash) + * @param xquery extrended query data (can be NULL, depending on type) + * @param xquery_size number of bytes in xquery + * @param reply_block response to validate + * @param reply_block_size number of bytes in reply block + * @return characterization of result + */ +static enum GNUNET_BLOCK_EvaluationResult +block_plugin_set_test_evaluate (void *cls, + struct GNUNET_BLOCK_Context *ctx, + enum GNUNET_BLOCK_Type type, + struct GNUNET_BLOCK_Group *group, + enum GNUNET_BLOCK_EvaluationOptions eo, + const struct GNUNET_HashCode *query, + const void *xquery, + size_t xquery_size, + const void *reply_block, + size_t reply_block_size) +{ + if ((NULL == reply_block) || + (reply_block_size == 0) || + (0 != ((char *) reply_block)[0])) + return GNUNET_BLOCK_EVALUATION_RESULT_INVALID; + return GNUNET_BLOCK_EVALUATION_OK_MORE; +} + + +/** + * Function called to obtain the key for a block. + * + * @param cls closure + * @param type block type + * @param block block to get the key for + * @param block_size number of bytes in block + * @param key set to the key (query) for the given block + * @return #GNUNET_OK on success, #GNUNET_SYSERR if type not supported + * (or if extracting a key from a block of this type does not work) + */ +static int +block_plugin_set_test_get_key (void *cls, + enum GNUNET_BLOCK_Type type, + const void *block, + size_t block_size, + struct GNUNET_HashCode *key) +{ + return GNUNET_SYSERR; +} + + +/** + * Entry point for the plugin. + */ +void * +libgnunet_plugin_block_set_test_init (void *cls) +{ + static enum GNUNET_BLOCK_Type types[] = { + GNUNET_BLOCK_TYPE_SET_TEST, + GNUNET_BLOCK_TYPE_ANY /* end of list */ + }; + struct GNUNET_BLOCK_PluginFunctions *api; + + api = GNUNET_new (struct GNUNET_BLOCK_PluginFunctions); + api->evaluate = &block_plugin_set_test_evaluate; + api->get_key = &block_plugin_set_test_get_key; + api->types = types; + return api; +} + + +/** + * Exit point from the plugin. + */ +void * +libgnunet_plugin_block_set_test_done (void *cls) +{ + struct GNUNET_BLOCK_PluginFunctions *api = cls; + + GNUNET_free (api); + return NULL; +} + + +/* end of plugin_block_set_test.c */ diff --git a/src/setu/setu.h b/src/setu/setu.h new file mode 100644 index 000000000..f1c5df92b --- /dev/null +++ b/src/setu/setu.h @@ -0,0 +1,304 @@ +/* + This file is part of GNUnet. + Copyright (C) 2012-2014, 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file set/set.h + * @brief messages used for the set union api + * @author Florian Dold + * @author Christian Grothoff + */ +#ifndef SET_H +#define SET_H + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_set_service.h" + +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * Message sent by the client to the service to ask starting + * a new set to perform operations with. Includes the desired + * set operation type. + */ +struct GNUNET_SETU_CreateMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SETU_CREATE + */ + struct GNUNET_MessageHeader header; + +}; + + +/** + * Message sent by the client to the service to start listening for + * incoming requests to perform a certain type of set operation for a + * certain type of application. + */ +struct GNUNET_SETU_ListenMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SETU_LISTEN + */ + struct GNUNET_MessageHeader header; + + /** + * Always zero. + */ + uint32_t reserved GNUNET_PACKED; + + /** + * application id + */ + struct GNUNET_HashCode app_id; +}; + + +/** + * Message sent by a listening client to the service to accept + * performing the operation with the other peer. + */ +struct GNUNET_SETU_AcceptMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SETU_ACCEPT + */ + struct GNUNET_MessageHeader header; + + /** + * ID of the incoming request we want to accept. + */ + uint32_t accept_reject_id GNUNET_PACKED; + + /** + * Request ID to identify responses. + */ + uint32_t request_id GNUNET_PACKED; + + /** + * Always use delta operation instead of sending full sets, + * even it it's less efficient. + */ + uint8_t force_delta; + + /** + * Always send full sets, even if delta operations would + * be more efficient. + */ + uint8_t force_full; + + /** + * #GNUNET_YES to fail operations where Byzantine faults + * are suspected + */ + uint8_t byzantine; + + /** + * Lower bound for the set size, used only when + * byzantine mode is enabled. + */ + uint8_t byzantine_lower_bound; +}; + + +/** + * Message sent by a listening client to the service to reject + * performing the operation with the other peer. + */ +struct GNUNET_SETU_RejectMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SETU_REJECT + */ + struct GNUNET_MessageHeader header; + + /** + * ID of the incoming request we want to reject. + */ + uint32_t accept_reject_id GNUNET_PACKED; +}; + + +/** + * A request for an operation with another client. + */ +struct GNUNET_SETU_RequestMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SETU_REQUEST. + */ + struct GNUNET_MessageHeader header; + + /** + * ID of the to identify the request when accepting or + * rejecting it. + */ + uint32_t accept_id GNUNET_PACKED; + + /** + * Identity of the requesting peer. + */ + struct GNUNET_PeerIdentity peer_id; + + /* rest: context message, that is, application-specific + message to convince listener to pick up */ +}; + + +/** + * Message sent by client to service to initiate a set operation as a + * client (not as listener). A set (which determines the operation + * type) must already exist in association with this client. + */ +struct GNUNET_SETU_EvaluateMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SETU_EVALUATE + */ + struct GNUNET_MessageHeader header; + + /** + * Id of our set to evaluate, chosen implicitly by the client when it + * calls #GNUNET_SETU_commit(). + */ + uint32_t request_id GNUNET_PACKED; + + /** + * Peer to evaluate the operation with + */ + struct GNUNET_PeerIdentity target_peer; + + /** + * Application id + */ + struct GNUNET_HashCode app_id; + + /** + * Always use delta operation instead of sending full sets, + * even it it's less efficient. + */ + uint8_t force_delta; + + /** + * Always send full sets, even if delta operations would + * be more efficient. + */ + uint8_t force_full; + + /** + * #GNUNET_YES to fail operations where Byzantine faults + * are suspected + */ + uint8_t byzantine; + + /** + * Lower bound for the set size, used only when + * byzantine mode is enabled. + */ + uint8_t byzantine_lower_bound; + + /* rest: context message, that is, application-specific + message to convince listener to pick up */ +}; + + +/** + * Message sent by the service to the client to indicate an + * element that is removed (set intersection) or added + * (set union) or part of the final result, depending on + * options specified for the operation. + */ +struct GNUNET_SETU_ResultMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SETU_RESULT + */ + struct GNUNET_MessageHeader header; + + /** + * Current set size. + */ + uint64_t current_size; + + /** + * id the result belongs to + */ + uint32_t request_id GNUNET_PACKED; + + /** + * Was the evaluation successful? Contains + * an `enum GNUNET_SETU_Status` in NBO. + */ + uint16_t result_status GNUNET_PACKED; + + /** + * Type of the element attachted to the message, if any. + */ + uint16_t element_type GNUNET_PACKED; + + /* rest: the actual element */ +}; + + +/** + * Message sent by client to the service to add + * an element to the set. + */ +struct GNUNET_SETU_ElementMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SETU_ADD + */ + struct GNUNET_MessageHeader header; + + /** + * Type of the element to add or remove. + */ + uint16_t element_type GNUNET_PACKED; + + /** + * For alignment, always zero. + */ + uint16_t reserved GNUNET_PACKED; + + /* rest: the actual element */ +}; + + +/** + * Sent to the service by the client in order to cancel a set operation. + */ +struct GNUNET_SETU_CancelMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SETU_CANCEL + */ + struct GNUNET_MessageHeader header; + + /** + * ID of the request we want to cancel. + */ + uint32_t request_id GNUNET_PACKED; +}; + + +GNUNET_NETWORK_STRUCT_END + +#endif diff --git a/src/setu/setu_api.c b/src/setu/setu_api.c new file mode 100644 index 000000000..48260de55 --- /dev/null +++ b/src/setu/setu_api.c @@ -0,0 +1,872 @@ +/* + This file is part of GNUnet. + Copyright (C) 2012-2016, 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file set/setu_api.c + * @brief api for the set union service + * @author Florian Dold + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_setu_service.h" +#include "setu.h" + + +#define LOG(kind, ...) GNUNET_log_from (kind, "set-api", __VA_ARGS__) + +/** + * Opaque handle to a set. + */ +struct GNUNET_SETU_Handle +{ + /** + * Message queue for @e client. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Linked list of operations on the set. + */ + struct GNUNET_SETU_OperationHandle *ops_head; + + /** + * Linked list of operations on the set. + */ + struct GNUNET_SETU_OperationHandle *ops_tail; + + /** + * Should the set be destroyed once all operations are gone? + * #GNUNET_SYSERR if #GNUNET_SETU_destroy() must raise this flag, + * #GNUNET_YES if #GNUNET_SETU_destroy() did raise this flag. + */ + int destroy_requested; + + /** + * Has the set become invalid (e.g. service died)? + */ + int invalid; + +}; + + +/** + * Handle for a set operation request from another peer. + */ +struct GNUNET_SETU_Request +{ + /** + * Id of the request, used to identify the request when + * accepting/rejecting it. + */ + uint32_t accept_id; + + /** + * Has the request been accepted already? + * #GNUNET_YES/#GNUNET_NO + */ + int accepted; +}; + + +/** + * Handle to an operation. Only known to the service after committing + * the handle with a set. + */ +struct GNUNET_SETU_OperationHandle +{ + /** + * Function to be called when we have a result, + * or an error. + */ + GNUNET_SETU_ResultIterator result_cb; + + /** + * Closure for @e result_cb. + */ + void *result_cls; + + /** + * Local set used for the operation, + * NULL if no set has been provided by conclude yet. + */ + struct GNUNET_SETU_Handle *set; + + /** + * Message sent to the server on calling conclude, + * NULL if conclude has been called. + */ + struct GNUNET_MQ_Envelope *conclude_mqm; + + /** + * Address of the request if in the conclude message, + * used to patch the request id into the message when the set is known. + */ + uint32_t *request_id_addr; + + /** + * Handles are kept in a linked list. + */ + struct GNUNET_SETU_OperationHandle *prev; + + /** + * Handles are kept in a linked list. + */ + struct GNUNET_SETU_OperationHandle *next; + + /** + * Request ID to identify the operation within the set. + */ + uint32_t request_id; +}; + + +/** + * Opaque handle to a listen operation. + */ +struct GNUNET_SETU_ListenHandle +{ + /** + * Message queue for the client. + */ + struct GNUNET_MQ_Handle*mq; + + /** + * Configuration handle for the listener, stored + * here to be able to reconnect transparently on + * connection failure. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Function to call on a new incoming request, + * or on error. + */ + GNUNET_SETU_ListenCallback listen_cb; + + /** + * Closure for @e listen_cb. + */ + void *listen_cls; + + /** + * Application ID we listen for. + */ + struct GNUNET_HashCode app_id; + + /** + * Time to wait until we try to reconnect on failure. + */ + struct GNUNET_TIME_Relative reconnect_backoff; + + /** + * Task for reconnecting when the listener fails. + */ + struct GNUNET_SCHEDULER_Task *reconnect_task; + +}; + + +/** + * Check that the given @a msg is well-formed. + * + * @param cls closure + * @param msg message to check + * @return #GNUNET_OK if message is well-formed + */ +static int +check_result (void *cls, + const struct GNUNET_SETU_ResultMessage *msg) +{ + /* minimum size was already checked, everything else is OK! */ + return GNUNET_OK; +} + + +/** + * Handle result message for a set operation. + * + * @param cls the set + * @param mh the message + */ +static void +handle_result (void *cls, + const struct GNUNET_SETU_ResultMessage *msg) +{ + struct GNUNET_SETU_Handle *set = cls; + struct GNUNET_SETU_OperationHandle *oh; + struct GNUNET_SETU_Element e; + enum GNUNET_SETU_Status result_status; + int destroy_set; + + GNUNET_assert (NULL != set->mq); + result_status = (enum GNUNET_SETU_Status) ntohs (msg->result_status); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Got result message with status %d\n", + result_status); + oh = GNUNET_MQ_assoc_get (set->mq, + ntohl (msg->request_id)); + if (NULL == oh) + { + /* 'oh' can be NULL if we canceled the operation, but the service + did not get the cancel message yet. */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Ignoring result from canceled operation\n"); + return; + } + + switch (result_status) + { + case GNUNET_SETU_STATUS_ADD_LOCAL: + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Treating result as element\n"); + e.data = &msg[1]; + e.size = ntohs (msg->header.size) + - sizeof(struct GNUNET_SETU_ResultMessage); + e.element_type = ntohs (msg->element_type); + if (NULL != oh->result_cb) + oh->result_cb (oh->result_cls, + &e, + GNUNET_ntohll (msg->current_size), + result_status); + return; + case GNUNET_SETU_STATUS_FAILURE: + case GNUNET_SETU_STATUS_DONE: + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Treating result as final status\n"); + GNUNET_MQ_assoc_remove (set->mq, + ntohl (msg->request_id)); + GNUNET_CONTAINER_DLL_remove (set->ops_head, + set->ops_tail, + oh); + /* Need to do this calculation _before_ the result callback, + as IF the application still has a valid set handle, it + may trigger destruction of the set during the callback. */ + destroy_set = (GNUNET_YES == set->destroy_requested) && + (NULL == set->ops_head); + if (NULL != oh->result_cb) + { + oh->result_cb (oh->result_cls, + NULL, + GNUNET_ntohll (msg->current_size), + result_status); + } + else + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "No callback for final status\n"); + } + if (destroy_set) + GNUNET_SETU_destroy (set); + GNUNET_free (oh); + return; + } +} + + +/** + * Destroy the given set operation. + * + * @param oh set operation to destroy + */ +static void +set_operation_destroy (struct GNUNET_SETU_OperationHandle *oh) +{ + struct GNUNET_SETU_Handle *set = oh->set; + struct GNUNET_SETU_OperationHandle *h_assoc; + + if (NULL != oh->conclude_mqm) + GNUNET_MQ_discard (oh->conclude_mqm); + /* is the operation already commited? */ + if (NULL != set) + { + GNUNET_CONTAINER_DLL_remove (set->ops_head, + set->ops_tail, + oh); + h_assoc = GNUNET_MQ_assoc_remove (set->mq, + oh->request_id); + GNUNET_assert ((NULL == h_assoc) || + (h_assoc == oh)); + } + GNUNET_free (oh); +} + + +/** + * Cancel the given set operation. We need to send an explicit cancel + * message, as all operations one one set communicate using one + * handle. + * + * @param oh set operation to cancel + */ +void +GNUNET_SETU_operation_cancel (struct GNUNET_SETU_OperationHandle *oh) +{ + struct GNUNET_SETU_Handle *set = oh->set; + struct GNUNET_SETU_CancelMessage *m; + struct GNUNET_MQ_Envelope *mqm; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Cancelling SET operation\n"); + if (NULL != set) + { + mqm = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_SETU_CANCEL); + m->request_id = htonl (oh->request_id); + GNUNET_MQ_send (set->mq, mqm); + } + set_operation_destroy (oh); + if ((NULL != set) && + (GNUNET_YES == set->destroy_requested) && + (NULL == set->ops_head)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Destroying set after operation cancel\n"); + GNUNET_SETU_destroy (set); + } +} + + +/** + * We encountered an error communicating with the set service while + * performing a set operation. Report to the application. + * + * @param cls the `struct GNUNET_SETU_Handle` + * @param error error code + */ +static void +handle_client_set_error (void *cls, + enum GNUNET_MQ_Error error) +{ + struct GNUNET_SETU_Handle *set = cls; + + LOG (GNUNET_ERROR_TYPE_ERROR, + "Handling client set error %d\n", + error); + while (NULL != set->ops_head) + { + if ((NULL != set->ops_head->result_cb) && + (GNUNET_NO == set->destroy_requested)) + set->ops_head->result_cb (set->ops_head->result_cls, + NULL, + 0, + GNUNET_SETU_STATUS_FAILURE); + set_operation_destroy (set->ops_head); + } + set->invalid = GNUNET_YES; +} + + +/** + * Create an empty set, supporting the specified operation. + * + * @param cfg configuration to use for connecting to the + * set service + * @return a handle to the set + */ +struct GNUNET_SETU_Handle * +GNUNET_SETU_create (const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_SETU_Handle *set = GNUNET_new (struct GNUNET_SETU_Handle); + struct GNUNET_MQ_MessageHandler mq_handlers[] = { + GNUNET_MQ_hd_var_size (result, + GNUNET_MESSAGE_TYPE_SETU_RESULT, + struct GNUNET_SETU_ResultMessage, + set), + GNUNET_MQ_handler_end () + }; + struct GNUNET_MQ_Envelope *mqm; + struct GNUNET_SETU_CreateMessage *create_msg; + + set->mq = GNUNET_CLIENT_connect (cfg, + "setu", + mq_handlers, + &handle_client_set_error, + set); + if (NULL == set->mq) + { + GNUNET_free (set); + return NULL; + } + mqm = GNUNET_MQ_msg (create_msg, + GNUNET_MESSAGE_TYPE_SETU_CREATE); + GNUNET_MQ_send (set->mq, + mqm); + return set; +} + + +/** + * Add an element to the given set. After the element has been added + * (in the sense of being transmitted to the set service), @a cont + * will be called. Multiple calls to GNUNET_SETU_add_element() can be + * queued. + * + * @param set set to add element to + * @param element element to add to the set + * @param cb continuation called after the element has been added + * @param cb_cls closure for @a cb + * @return #GNUNET_OK on success, #GNUNET_SYSERR if the + * set is invalid (e.g. the set service crashed) + */ +int +GNUNET_SETU_add_element (struct GNUNET_SETU_Handle *set, + const struct GNUNET_SETU_Element *element, + GNUNET_SCHEDULER_TaskCallback cb, + void *cb_cls) +{ + struct GNUNET_MQ_Envelope *mqm; + struct GNUNET_SETU_ElementMessage *msg; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "adding element of type %u to set %p\n", + (unsigned int) element->element_type, + set); + GNUNET_assert (NULL != set); + if (GNUNET_YES == set->invalid) + { + if (NULL != cb) + cb (cb_cls); + return GNUNET_SYSERR; + } + mqm = GNUNET_MQ_msg_extra (msg, + element->size, + GNUNET_MESSAGE_TYPE_SETU_ADD); + msg->element_type = htons (element->element_type); + GNUNET_memcpy (&msg[1], + element->data, + element->size); + GNUNET_MQ_notify_sent (mqm, + cb, + cb_cls); + GNUNET_MQ_send (set->mq, + mqm); + return GNUNET_OK; +} + + +/** + * Destroy the set handle if no operations are left, mark the set + * for destruction otherwise. + * + * @param set set handle to destroy + */ +void +GNUNET_SETU_destroy (struct GNUNET_SETU_Handle *set) +{ + /* destroying set while iterator is active is currently + not supported; we should expand the API to allow + clients to explicitly cancel the iteration! */ + GNUNET_assert (NULL != set); + if ((NULL != set->ops_head) || + (GNUNET_SYSERR == set->destroy_requested)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Set operations are pending, delaying set destruction\n"); + set->destroy_requested = GNUNET_YES; + return; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Really destroying set\n"); + if (NULL != set->mq) + { + GNUNET_MQ_destroy (set->mq); + set->mq = NULL; + } + GNUNET_free (set); +} + + +/** + * Prepare a set operation to be evaluated with another peer. + * The evaluation will not start until the client provides + * a local set with #GNUNET_SETU_commit(). + * + * @param other_peer peer with the other set + * @param app_id hash for the application using the set + * @param context_msg additional information for the request + * @param result_cb called on error or success + * @param result_cls closure for @e result_cb + * @return a handle to cancel the operation + */ +struct GNUNET_SETU_OperationHandle * +GNUNET_SETU_prepare (const struct GNUNET_PeerIdentity *other_peer, + const struct GNUNET_HashCode *app_id, + const struct GNUNET_MessageHeader *context_msg, + const struct GNUNET_SETU_Option options[], + GNUNET_SETU_ResultIterator result_cb, + void *result_cls) +{ + struct GNUNET_MQ_Envelope *mqm; + struct GNUNET_SETU_OperationHandle *oh; + struct GNUNET_SETU_EvaluateMessage *msg; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Client prepares set union operation\n"); + oh = GNUNET_new (struct GNUNET_SETU_OperationHandle); + oh->result_cb = result_cb; + oh->result_cls = result_cls; + mqm = GNUNET_MQ_msg_nested_mh (msg, + GNUNET_MESSAGE_TYPE_SETU_EVALUATE, + context_msg); + msg->app_id = *app_id; + msg->target_peer = *other_peer; + for (const struct GNUNET_SETU_Option *opt = options; opt->type != 0; opt++) + { + switch (opt->type) + { + case GNUNET_SETU_OPTION_BYZANTINE: + msg->byzantine = GNUNET_YES; + msg->byzantine_lower_bound = opt->v.num; + break; + case GNUNET_SETU_OPTION_FORCE_FULL: + msg->force_full = GNUNET_YES; + break; + case GNUNET_SETU_OPTION_FORCE_DELTA: + msg->force_delta = GNUNET_YES; + break; + default: + LOG (GNUNET_ERROR_TYPE_ERROR, + "Option with type %d not recognized\n", + (int) opt->type); + } + } + oh->conclude_mqm = mqm; + oh->request_id_addr = &msg->request_id; + return oh; +} + + +/** + * Connect to the set service in order to listen for requests. + * + * @param cls the `struct GNUNET_SETU_ListenHandle *` to connect + */ +static void +listen_connect (void *cls); + + +/** + * Check validity of request message for a listen operation + * + * @param cls the listen handle + * @param msg the message + * @return #GNUNET_OK if the message is well-formed + */ +static int +check_request (void *cls, + const struct GNUNET_SETU_RequestMessage *msg) +{ + const struct GNUNET_MessageHeader *context_msg; + + if (ntohs (msg->header.size) == sizeof(*msg)) + return GNUNET_OK; /* no context message is OK */ + context_msg = GNUNET_MQ_extract_nested_mh (msg); + if (NULL == context_msg) + { + /* malformed context message is NOT ok */ + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Handle request message for a listen operation + * + * @param cls the listen handle + * @param msg the message + */ +static void +handle_request (void *cls, + const struct GNUNET_SETU_RequestMessage *msg) +{ + struct GNUNET_SETU_ListenHandle *lh = cls; + struct GNUNET_SETU_Request req; + const struct GNUNET_MessageHeader *context_msg; + struct GNUNET_MQ_Envelope *mqm; + struct GNUNET_SETU_RejectMessage *rmsg; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Processing incoming operation request with id %u\n", + ntohl (msg->accept_id)); + /* we got another valid request => reset the backoff */ + lh->reconnect_backoff = GNUNET_TIME_UNIT_MILLISECONDS; + req.accept_id = ntohl (msg->accept_id); + req.accepted = GNUNET_NO; + context_msg = GNUNET_MQ_extract_nested_mh (msg); + /* calling #GNUNET_SETU_accept() in the listen cb will set req->accepted */ + lh->listen_cb (lh->listen_cls, + &msg->peer_id, + context_msg, + &req); + if (GNUNET_YES == req.accepted) + return; /* the accept-case is handled in #GNUNET_SETU_accept() */ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Rejected request %u\n", + ntohl (msg->accept_id)); + mqm = GNUNET_MQ_msg (rmsg, + GNUNET_MESSAGE_TYPE_SETU_REJECT); + rmsg->accept_reject_id = msg->accept_id; + GNUNET_MQ_send (lh->mq, + mqm); +} + + +/** + * Our connection with the set service encountered an error, + * re-initialize with exponential back-off. + * + * @param cls the `struct GNUNET_SETU_ListenHandle *` + * @param error reason for the disconnect + */ +static void +handle_client_listener_error (void *cls, + enum GNUNET_MQ_Error error) +{ + struct GNUNET_SETU_ListenHandle *lh = cls; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Listener broke down (%d), re-connecting\n", + (int) error); + GNUNET_MQ_destroy (lh->mq); + lh->mq = NULL; + lh->reconnect_task = GNUNET_SCHEDULER_add_delayed (lh->reconnect_backoff, + &listen_connect, + lh); + lh->reconnect_backoff = GNUNET_TIME_STD_BACKOFF (lh->reconnect_backoff); +} + + +/** + * Connect to the set service in order to listen for requests. + * + * @param cls the `struct GNUNET_SETU_ListenHandle *` to connect + */ +static void +listen_connect (void *cls) +{ + struct GNUNET_SETU_ListenHandle *lh = cls; + struct GNUNET_MQ_MessageHandler mq_handlers[] = { + GNUNET_MQ_hd_var_size (request, + GNUNET_MESSAGE_TYPE_SETU_REQUEST, + struct GNUNET_SETU_RequestMessage, + lh), + GNUNET_MQ_handler_end () + }; + struct GNUNET_MQ_Envelope *mqm; + struct GNUNET_SETU_ListenMessage *msg; + + lh->reconnect_task = NULL; + GNUNET_assert (NULL == lh->mq); + lh->mq = GNUNET_CLIENT_connect (lh->cfg, + "setu", + mq_handlers, + &handle_client_listener_error, + lh); + if (NULL == lh->mq) + return; + mqm = GNUNET_MQ_msg (msg, + GNUNET_MESSAGE_TYPE_SETU_LISTEN); + msg->app_id = lh->app_id; + GNUNET_MQ_send (lh->mq, + mqm); +} + + +/** + * Wait for set operation requests for the given application id + * + * @param cfg configuration to use for connecting to + * the set service, needs to be valid for the lifetime of the listen handle + * @param app_id id of the application that handles set operation requests + * @param listen_cb called for each incoming request matching the operation + * and application id + * @param listen_cls handle for @a listen_cb + * @return a handle that can be used to cancel the listen operation + */ +struct GNUNET_SETU_ListenHandle * +GNUNET_SETU_listen (const struct GNUNET_CONFIGURATION_Handle *cfg, + const struct GNUNET_HashCode *app_id, + GNUNET_SETU_ListenCallback listen_cb, + void *listen_cls) +{ + struct GNUNET_SETU_ListenHandle *lh; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Starting listener for app %s\n", + GNUNET_h2s (app_id)); + lh = GNUNET_new (struct GNUNET_SETU_ListenHandle); + lh->listen_cb = listen_cb; + lh->listen_cls = listen_cls; + lh->cfg = cfg; + lh->app_id = *app_id; + lh->reconnect_backoff = GNUNET_TIME_UNIT_MILLISECONDS; + listen_connect (lh); + if (NULL == lh->mq) + { + GNUNET_free (lh); + return NULL; + } + return lh; +} + + +/** + * Cancel the given listen operation. + * + * @param lh handle for the listen operation + */ +void +GNUNET_SETU_listen_cancel (struct GNUNET_SETU_ListenHandle *lh) +{ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Canceling listener %s\n", + GNUNET_h2s (&lh->app_id)); + if (NULL != lh->mq) + { + GNUNET_MQ_destroy (lh->mq); + lh->mq = NULL; + } + if (NULL != lh->reconnect_task) + { + GNUNET_SCHEDULER_cancel (lh->reconnect_task); + lh->reconnect_task = NULL; + } + GNUNET_free (lh); +} + + +/** + * Accept a request we got via #GNUNET_SETU_listen. Must be called during + * #GNUNET_SETU_listen, as the 'struct GNUNET_SETU_Request' becomes invalid + * afterwards. + * Call #GNUNET_SETU_commit to provide the local set to use for the operation, + * and to begin the exchange with the remote peer. + * + * @param request request to accept + * @param result_mode specified how results will be returned, + * see `enum GNUNET_SETU_ResultMode`. + * @param result_cb callback for the results + * @param result_cls closure for @a result_cb + * @return a handle to cancel the operation + */ +struct GNUNET_SETU_OperationHandle * +GNUNET_SETU_accept (struct GNUNET_SETU_Request *request, + const struct GNUNET_SETU_Option options[], + GNUNET_SETU_ResultIterator result_cb, + void *result_cls) +{ + struct GNUNET_MQ_Envelope *mqm; + struct GNUNET_SETU_OperationHandle *oh; + struct GNUNET_SETU_AcceptMessage *msg; + + GNUNET_assert (GNUNET_NO == request->accepted); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Client accepts set union operation with id %u\n", + request->accept_id); + request->accepted = GNUNET_YES; + mqm = GNUNET_MQ_msg (msg, + GNUNET_MESSAGE_TYPE_SETU_ACCEPT); + msg->accept_reject_id = htonl (request->accept_id); + oh = GNUNET_new (struct GNUNET_SETU_OperationHandle); + oh->result_cb = result_cb; + oh->result_cls = result_cls; + oh->conclude_mqm = mqm; + oh->request_id_addr = &msg->request_id; + return oh; +} + + +/** + * Commit a set to be used with a set operation. + * This function is called once we have fully constructed + * the set that we want to use for the operation. At this + * time, the P2P protocol can then begin to exchange the + * set information and call the result callback with the + * result information. + * + * @param oh handle to the set operation + * @param set the set to use for the operation + * @return #GNUNET_OK on success, #GNUNET_SYSERR if the + * set is invalid (e.g. the set service crashed) + */ +int +GNUNET_SETU_commit (struct GNUNET_SETU_OperationHandle *oh, + struct GNUNET_SETU_Handle *set) +{ + if (NULL != oh->set) + { + /* Some other set was already committed for this + * operation, there is a logic bug in the client of this API */ + GNUNET_break (0); + return GNUNET_OK; + } + GNUNET_assert (NULL != set); + if (GNUNET_YES == set->invalid) + return GNUNET_SYSERR; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Client commits to SET\n"); + GNUNET_assert (NULL != oh->conclude_mqm); + oh->set = set; + GNUNET_CONTAINER_DLL_insert (set->ops_head, + set->ops_tail, + oh); + oh->request_id = GNUNET_MQ_assoc_add (set->mq, + oh); + *oh->request_id_addr = htonl (oh->request_id); + GNUNET_MQ_send (set->mq, + oh->conclude_mqm); + oh->conclude_mqm = NULL; + oh->request_id_addr = NULL; + return GNUNET_OK; +} + + +/** + * Hash a set element. + * + * @param element the element that should be hashed + * @param[out] ret_hash a pointer to where the hash of @a element + * should be stored + */ +void +GNUNET_SETU_element_hash (const struct GNUNET_SETU_Element *element, + struct GNUNET_HashCode *ret_hash) +{ + struct GNUNET_HashContext *ctx = GNUNET_CRYPTO_hash_context_start (); + + /* It's not guaranteed that the element data is always after the element header, + so we need to hash the chunks separately. */ + GNUNET_CRYPTO_hash_context_read (ctx, + &element->size, + sizeof(uint16_t)); + GNUNET_CRYPTO_hash_context_read (ctx, + &element->element_type, + sizeof(uint16_t)); + GNUNET_CRYPTO_hash_context_read (ctx, + element->data, + element->size); + GNUNET_CRYPTO_hash_context_finish (ctx, + ret_hash); +} + + +/* end of setu_api.c */ diff --git a/src/setu/test_setu_api.c b/src/setu/test_setu_api.c new file mode 100644 index 000000000..95119873c --- /dev/null +++ b/src/setu/test_setu_api.c @@ -0,0 +1,360 @@ +/* + This file is part of GNUnet. + Copyright (C) 2012 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file set/test_setu_api.c + * @brief testcase for setu_api.c + * @author Florian Dold + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testing_lib.h" +#include "gnunet_setu_service.h" + + +static struct GNUNET_PeerIdentity local_id; + +static struct GNUNET_HashCode app_id; + +static struct GNUNET_SETU_Handle *set1; + +static struct GNUNET_SETU_Handle *set2; + +static struct GNUNET_SETU_ListenHandle *listen_handle; + +static struct GNUNET_SETU_OperationHandle *oh1; + +static struct GNUNET_SETU_OperationHandle *oh2; + +static const struct GNUNET_CONFIGURATION_Handle *config; + +static int ret; + +static struct GNUNET_SCHEDULER_Task *tt; + + +static void +result_cb_set1 (void *cls, + const struct GNUNET_SETU_Element *element, + uint64_t size, + enum GNUNET_SETU_Status status) +{ + switch (status) + { + case GNUNET_SETU_STATUS_ADD_LOCAL: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "set 1: got element\n"); + break; + + case GNUNET_SETU_STATUS_FAILURE: + GNUNET_break (0); + oh1 = NULL; + fprintf (stderr, "set 1: received failure status!\n"); + ret = 1; + if (NULL != tt) + { + GNUNET_SCHEDULER_cancel (tt); + tt = NULL; + } + GNUNET_SCHEDULER_shutdown (); + break; + + case GNUNET_SETU_STATUS_DONE: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "set 1: done\n"); + oh1 = NULL; + if (NULL != set1) + { + GNUNET_SETU_destroy (set1); + set1 = NULL; + } + if (NULL == set2) + { + GNUNET_SCHEDULER_cancel (tt); + tt = NULL; + GNUNET_SCHEDULER_shutdown (); + } + break; + + default: + GNUNET_assert (0); + } +} + + +static void +result_cb_set2 (void *cls, + const struct GNUNET_SETU_Element *element, + uint64_t size, + enum GNUNET_SETU_Status status) +{ + switch (status) + { + case GNUNET_SETU_STATUS_ADD_LOCAL: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "set 2: got element\n"); + break; + + case GNUNET_SETU_STATUS_FAILURE: + GNUNET_break (0); + oh2 = NULL; + fprintf (stderr, "set 2: received failure status\n"); + GNUNET_SCHEDULER_shutdown (); + ret = 1; + break; + + case GNUNET_SETU_STATUS_DONE: + oh2 = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "set 2: done\n"); + GNUNET_SETU_destroy (set2); + set2 = NULL; + if (NULL == set1) + { + GNUNET_SCHEDULER_cancel (tt); + tt = NULL; + GNUNET_SCHEDULER_shutdown (); + } + break; + + default: + GNUNET_assert (0); + } +} + + +static void +listen_cb (void *cls, + const struct GNUNET_PeerIdentity *other_peer, + const struct GNUNET_MessageHeader *context_msg, + struct GNUNET_SETU_Request *request) +{ + GNUNET_assert (NULL != context_msg); + GNUNET_assert (ntohs (context_msg->type) == GNUNET_MESSAGE_TYPE_DUMMY); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "listen cb called\n"); + oh2 = GNUNET_SETU_accept (request, + (struct GNUNET_SETU_Option[]){ 0 }, + &result_cb_set2, + NULL); + GNUNET_SETU_commit (oh2, set2); +} + + +/** + * Start the set operation. + * + * @param cls closure, unused + */ +static void +start (void *cls) +{ + struct GNUNET_MessageHeader context_msg; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting reconciliation\n"); + context_msg.size = htons (sizeof context_msg); + context_msg.type = htons (GNUNET_MESSAGE_TYPE_DUMMY); + listen_handle = GNUNET_SETU_listen (config, + &app_id, + &listen_cb, + NULL); + oh1 = GNUNET_SETU_prepare (&local_id, + &app_id, + &context_msg, + (struct GNUNET_SETU_Option[]){ 0 }, + &result_cb_set1, + NULL); + GNUNET_SETU_commit (oh1, set1); +} + + +/** + * Initialize the second set, continue + * + * @param cls closure, unused + */ +static void +init_set2 (void *cls) +{ + struct GNUNET_SETU_Element element; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "initializing set 2\n"); + + element.element_type = 0; + element.data = "hello"; + element.size = strlen (element.data); + GNUNET_SETU_add_element (set2, &element, NULL, NULL); + element.data = "quux"; + element.size = strlen (element.data); + GNUNET_SETU_add_element (set2, &element, NULL, NULL); + element.data = "baz"; + element.size = strlen (element.data); + GNUNET_SETU_add_element (set2, &element, &start, NULL); +} + + +/** + * Initialize the first set, continue. + */ +static void +init_set1 (void) +{ + struct GNUNET_SETU_Element element; + + element.element_type = 0; + element.data = "hello"; + element.size = strlen (element.data); + GNUNET_SETU_add_element (set1, &element, NULL, NULL); + element.data = "bar"; + element.size = strlen (element.data); + GNUNET_SETU_add_element (set1, &element, &init_set2, NULL); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "initialized set 1\n"); +} + + +/** + * Function run on timeout. + * + * @param cls closure + */ +static void +timeout_fail (void *cls) +{ + tt = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Testcase failed with timeout\n"); + GNUNET_SCHEDULER_shutdown (); + ret = 1; +} + + +/** + * Function run on shutdown. + * + * @param cls closure + */ +static void +do_shutdown (void *cls) +{ + if (NULL != tt) + { + GNUNET_SCHEDULER_cancel (tt); + tt = NULL; + } + if (NULL != oh1) + { + GNUNET_SETU_operation_cancel (oh1); + oh1 = NULL; + } + if (NULL != oh2) + { + GNUNET_SETU_operation_cancel (oh2); + oh2 = NULL; + } + if (NULL != set1) + { + GNUNET_SETU_destroy (set1); + set1 = NULL; + } + if (NULL != set2) + { + GNUNET_SETU_destroy (set2); + set2 = NULL; + } + if (NULL != listen_handle) + { + GNUNET_SETU_listen_cancel (listen_handle); + listen_handle = NULL; + } +} + + +/** + * Signature of the 'main' function for a (single-peer) testcase that + * is run using 'GNUNET_TESTING_peer_run'. + * + * @param cls closure + * @param cfg configuration of the peer that was started + * @param peer identity of the peer that was created + */ +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + struct GNUNET_SETU_OperationHandle *my_oh; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Running preparatory tests\n"); + tt = GNUNET_SCHEDULER_add_delayed ( + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5), + &timeout_fail, + NULL); + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); + + config = cfg; + GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_get_peer_identity (cfg, + &local_id)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "my id (from CRYPTO): %s\n", + GNUNET_i2s (&local_id)); + GNUNET_TESTING_peer_get_identity (peer, + &local_id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "my id (from TESTING): %s\n", + GNUNET_i2s (&local_id)); + set1 = GNUNET_SETU_create (cfg); + set2 = GNUNET_SETU_create (cfg); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created sets %p and %p for union operation\n", + set1, + set2); + GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, &app_id); + + /* test if canceling an uncommited request works! */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Launching and instantly stopping set operation\n"); + my_oh = GNUNET_SETU_prepare (&local_id, + &app_id, + NULL, + (struct GNUNET_SETU_Option[]){ 0 }, + NULL, + NULL); + GNUNET_SETU_operation_cancel (my_oh); + + /* test the real set reconciliation */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Running real set-reconciliation\n"); + init_set1 (); +} + + +int +main (int argc, char **argv) +{ + GNUNET_log_setup ("test_setu_api", + "WARNING", + NULL); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Launching peer\n"); + if (0 != + GNUNET_TESTING_peer_run ("test_setu_api", + "test_setu.conf", + &run, + NULL)) + { + return 1; + } + return ret; +} -- cgit v1.2.3 From 4dca8c3557a50ae5be9c105aa5fb2f85d99d917a Mon Sep 17 00:00:00 2001 From: Martin Schanzenbach Date: Sun, 16 Aug 2020 21:54:35 +0200 Subject: fix build pipeline --- .buildbot/build.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.buildbot/build.sh b/.buildbot/build.sh index 394558ff0..f2cdb3f2a 100755 --- a/.buildbot/build.sh +++ b/.buildbot/build.sh @@ -1,5 +1,3 @@ #!/bin/bash -./bootstrap -./configure --prefix=/tmp/gnunet --enable-experimental -make +./bootstrap && ./configure --prefix=/tmp/gnunet --enable-experimental && make -- cgit v1.2.3 From cadd7feeb2fc23796601db1744812e38432661df Mon Sep 17 00:00:00 2001 From: Martin Schanzenbach Date: Sun, 16 Aug 2020 22:05:15 +0200 Subject: -proper return code when bootstrap fails --- bootstrap | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bootstrap b/bootstrap index b4f5b4dc3..df0e58dda 100755 --- a/bootstrap +++ b/bootstrap @@ -79,8 +79,8 @@ check_libtool() existence libtoolize || \ existence glibtoolize || \ existence slibtool; then - autoreconf -if - . "bin/pogen.sh" + autoreconf -if || exit 1 + . "bin/pogen.sh" || exit 1 else echo "*** No libtoolize (libtool) or libtool found, please install it ***" >&2; exit 1 -- cgit v1.2.3 From a065c0fb4caa2e4f9f19eaf52f28f7893d49db6d Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 16 Aug 2020 23:26:43 +0200 Subject: add missing file --- src/setu/setu.conf.in | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/setu/setu.conf.in diff --git a/src/setu/setu.conf.in b/src/setu/setu.conf.in new file mode 100644 index 000000000..6c48f6156 --- /dev/null +++ b/src/setu/setu.conf.in @@ -0,0 +1,12 @@ +[setu] +START_ON_DEMAND = @START_ON_DEMAND@ +@UNIXONLY@PORT = 2106 +HOSTNAME = localhost +BINARY = gnunet-service-setu +ACCEPT_FROM = 127.0.0.1; +ACCEPT_FROM6 = ::1; +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-setu.sock +UNIX_MATCH_UID = YES +UNIX_MATCH_GID = YES + +#PREFIX = valgrind -- cgit v1.2.3 From f6acda6d3299bda2e6ea6e38bef5cabfb01b8ab9 Mon Sep 17 00:00:00 2001 From: Martin Schanzenbach Date: Mon, 17 Aug 2020 13:53:25 +0200 Subject: -bail on bootstrap error(s) --- bootstrap | 8 ++++---- po/POTFILES.in | 13 +------------ 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/bootstrap b/bootstrap index df0e58dda..8fc4c4426 100755 --- a/bootstrap +++ b/bootstrap @@ -97,15 +97,15 @@ submodules() if ! git --version >/dev/null; then echo "git not installed, skipping submodule update" else - git submodule update --init || true - git submodule update --recursive || true - git submodule sync || true + git submodule update --init || exit 1 + git submodule update --recursive || exit 1 + git submodule sync || exit 1 fi } init_buildcommon_include() { - cp contrib/build-common/Makefile.inc contrib/Makefile.inc || true + cp contrib/build-common/Makefile.inc contrib/Makefile.inc || exit 1 } main() diff --git a/po/POTFILES.in b/po/POTFILES.in index c5503c343..7d19122ca 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -333,19 +333,8 @@ src/set/ibf.c src/set/ibf_sim.c src/set/plugin_block_set_test.c src/set/set_api.c -src/seti/gnunet-service-set.c -src/seti/gnunet-service-set_intersection.c -src/seti/gnunet-service-set_union.c -src/seti/gnunet-service-set_union_strata_estimator.c -src/seti/gnunet-set-ibf-profiler.c -src/seti/gnunet-set-profiler.c -src/seti/ibf.c -src/seti/ibf_sim.c -src/seti/plugin_block_set_test.c -src/seti/set_api.c -src/setu/gnunet-service-set_union.c -src/setu/gnunet-service-set_union_strata_estimator.c src/setu/gnunet-service-setu.c +src/setu/gnunet-service-setu_strata_estimator.c src/setu/gnunet-setu-ibf-profiler.c src/setu/gnunet-setu-profiler.c src/setu/ibf.c -- cgit v1.2.3 From 6f2ee9eb3c3642147fe381976ffeb88d0c3288e5 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 17 Aug 2020 14:45:46 +0200 Subject: clean up GNUNET_CRYPTO_pow_hash API --- src/include/gnunet_crypto_lib.h | 12 +++++++++++- src/nse/gnunet-service-nse.c | 9 +++++++-- src/nse/perf_kdf.c | 3 ++- src/revocation/revocation_api.c | 6 ++++-- src/util/crypto_pow.c | 3 +-- src/util/gnunet-scrypt.c | 9 ++++++++- 6 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/include/gnunet_crypto_lib.h b/src/include/gnunet_crypto_lib.h index f8eef5406..6d3725aa0 100644 --- a/src/include/gnunet_crypto_lib.h +++ b/src/include/gnunet_crypto_lib.h @@ -50,6 +50,7 @@ extern "C" { #endif #endif +#include /** * The identity of the host (wraps the signing key of the peer). @@ -667,6 +668,15 @@ GNUNET_CRYPTO_hash (const void *block, struct GNUNET_HashCode *ret); +/** + * Value for a salt for #GNUNET_CRYPTO_pow_hash(). + */ +struct GNUNET_CRYPTO_PowSalt +{ + uint8_t salt[crypto_pwhash_argon2id_SALTBYTES]; +}; + + /** * Calculate the 'proof-of-work' hash (an expensive hash). * @@ -676,7 +686,7 @@ GNUNET_CRYPTO_hash (const void *block, * @param result where to write the resulting hash */ void -GNUNET_CRYPTO_pow_hash (const char *salt, +GNUNET_CRYPTO_pow_hash (const struct GNUNET_CRYPTO_PowSalt *salt, const void *buf, size_t buf_len, struct GNUNET_HashCode *result); diff --git a/src/nse/gnunet-service-nse.c b/src/nse/gnunet-service-nse.c index ebf39585e..dfd71e57a 100644 --- a/src/nse/gnunet-service-nse.c +++ b/src/nse/gnunet-service-nse.c @@ -113,6 +113,11 @@ static struct GNUNET_BIO_WriteHandle *histogram; #endif +/** + * Salt for PoW calcualations. + */ +static struct GNUNET_CRYPTO_PowSalt salt = { "gnunet-nse-proof" }; + /** * Per-peer information. @@ -806,7 +811,7 @@ check_proof_of_work (const struct GNUNET_CRYPTO_EddsaPublicKey *pkey, GNUNET_memcpy (&buf[sizeof(val)], pkey, sizeof(struct GNUNET_CRYPTO_EddsaPublicKey)); - GNUNET_CRYPTO_pow_hash ("gnunet-nse-proof", + GNUNET_CRYPTO_pow_hash (&salt, buf, sizeof(buf), &result); @@ -861,7 +866,7 @@ find_proof (void *cls) while ((counter != UINT64_MAX) && (i < ROUND_SIZE)) { GNUNET_memcpy (buf, &counter, sizeof(uint64_t)); - GNUNET_CRYPTO_pow_hash ("gnunet-nse-proof", + GNUNET_CRYPTO_pow_hash (&salt, buf, sizeof(buf), &result); diff --git a/src/nse/perf_kdf.c b/src/nse/perf_kdf.c index 89b70903a..10207675f 100644 --- a/src/nse/perf_kdf.c +++ b/src/nse/perf_kdf.c @@ -34,10 +34,11 @@ perfHash () { struct GNUNET_HashCode hc; char buf[64]; + struct GNUNET_CRYPTO_PowSalt salt = { "gnunet-nse-proof" }; memset (buf, 1, sizeof(buf)); for (unsigned int i = 0; i < 1024; i++) - GNUNET_CRYPTO_pow_hash ("gnunet-nse-proof", + GNUNET_CRYPTO_pow_hash (&salt, buf, sizeof(buf), &hc); diff --git a/src/revocation/revocation_api.c b/src/revocation/revocation_api.c index e0b195aa9..75cfd8761 100644 --- a/src/revocation/revocation_api.c +++ b/src/revocation/revocation_api.c @@ -103,6 +103,8 @@ struct GNUNET_REVOCATION_PowCalculationHandle }; +static struct GNUNET_CRYPTO_PowSalt salt = { "GnsRevocationPow" }; + /** * Generic error handler, called with the appropriate * error code and the same closure specified at the creation of @@ -483,7 +485,7 @@ GNUNET_REVOCATION_check_pow (const struct GNUNET_REVOCATION_PowP *pow, { pow_val = GNUNET_ntohll (pow->pow[i]); GNUNET_memcpy (buf, &pow->pow[i], sizeof(uint64_t)); - GNUNET_CRYPTO_pow_hash ("GnsRevocationPow", + GNUNET_CRYPTO_pow_hash (&salt, buf, sizeof(buf), &result); @@ -644,7 +646,7 @@ GNUNET_REVOCATION_pow_round (struct GNUNET_REVOCATION_PowCalculationHandle *pc) GNUNET_memcpy (&buf[sizeof(uint64_t) * 2], &pc->pow->key, sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)); - GNUNET_CRYPTO_pow_hash ("GnsRevocationPow", + GNUNET_CRYPTO_pow_hash (&salt, buf, sizeof(buf), &result); diff --git a/src/util/crypto_pow.c b/src/util/crypto_pow.c index cfa0676d0..051a0c209 100644 --- a/src/util/crypto_pow.c +++ b/src/util/crypto_pow.c @@ -38,12 +38,11 @@ * @param result where to write the resulting hash */ void -GNUNET_CRYPTO_pow_hash (const char *salt, +GNUNET_CRYPTO_pow_hash (const struct GNUNET_CRYPTO_PowSalt *salt, const void *buf, size_t buf_len, struct GNUNET_HashCode *result) { - GNUNET_assert (strlen (salt) == crypto_pwhash_argon2id_SALTBYTES); /* Threads hardcoded at 1 in libsodium */ GNUNET_break (0 == crypto_pwhash_argon2id ((unsigned char *) result, diff --git a/src/util/gnunet-scrypt.c b/src/util/gnunet-scrypt.c index aa64144a8..136c6debb 100644 --- a/src/util/gnunet-scrypt.c +++ b/src/util/gnunet-scrypt.c @@ -26,6 +26,13 @@ #include "gnunet_util_lib.h" #include + +/** + * Salt for PoW calcualations. + */ +static struct GNUNET_CRYPTO_PowSalt salt = { "gnunet-nse-proof" }; + + /** * Amount of work required (W-bit collisions) for NSE proofs, in collision-bits. */ @@ -117,7 +124,7 @@ find_proof (void *cls) while ((counter != UINT64_MAX) && (i < ROUND_SIZE)) { GNUNET_memcpy (buf, &counter, sizeof(uint64_t)); - GNUNET_CRYPTO_pow_hash ("gnunet-nse-proof", + GNUNET_CRYPTO_pow_hash (&salt, buf, sizeof(buf), &result); -- cgit v1.2.3 From 84e5706cbe249aa858d0c4a7d3405e1c2f7fc3a4 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 17 Aug 2020 14:57:37 +0200 Subject: use char --- src/include/gnunet_crypto_lib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/gnunet_crypto_lib.h b/src/include/gnunet_crypto_lib.h index 6d3725aa0..8c3f4f058 100644 --- a/src/include/gnunet_crypto_lib.h +++ b/src/include/gnunet_crypto_lib.h @@ -673,7 +673,7 @@ GNUNET_CRYPTO_hash (const void *block, */ struct GNUNET_CRYPTO_PowSalt { - uint8_t salt[crypto_pwhash_argon2id_SALTBYTES]; + char salt[crypto_pwhash_argon2id_SALTBYTES]; }; -- cgit v1.2.3 From c37363f1cd919495c43d138a8cc2bd71de65f83c Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 17 Aug 2020 15:00:27 +0200 Subject: fix ftbfs --- src/util/test_os_start_process.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/util/test_os_start_process.c b/src/util/test_os_start_process.c index 6d4a131bb..ff5021d03 100644 --- a/src/util/test_os_start_process.c +++ b/src/util/test_os_start_process.c @@ -124,11 +124,8 @@ run_task (void *cls) GNUNET_asprintf (&fn, "cat"); - hello_pipe_stdin = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, - GNUNET_NO); - hello_pipe_stdout = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, - GNUNET_YES); - + hello_pipe_stdin = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW); + hello_pipe_stdout = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW); if ((hello_pipe_stdout == NULL) || (hello_pipe_stdin == NULL)) { GNUNET_break (0); @@ -202,10 +199,8 @@ check_kill () { char *fn; - hello_pipe_stdin = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, - GNUNET_NO); - hello_pipe_stdout = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, - GNUNET_YES); + hello_pipe_stdin = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW); + hello_pipe_stdout = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW); if ((hello_pipe_stdout == NULL) || (hello_pipe_stdin == NULL)) { return 1; @@ -247,10 +242,8 @@ check_instant_kill () { char *fn; - hello_pipe_stdin = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, - GNUNET_NO); - hello_pipe_stdout = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, - GNUNET_YES); + hello_pipe_stdin = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW); + hello_pipe_stdout = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_RW); if ((hello_pipe_stdout == NULL) || (hello_pipe_stdin == NULL)) { return 1; -- cgit v1.2.3 From 2bce42e5b3630f3835f401e5eeed23a56b7bd875 Mon Sep 17 00:00:00 2001 From: Martin Schanzenbach Date: Tue, 18 Aug 2020 00:09:59 +0200 Subject: fix: build on openbsd --- configure.ac | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index bb205220c..72309c78d 100644 --- a/configure.ac +++ b/configure.ac @@ -142,9 +142,11 @@ AS_CASE(["$host_os"], UNIXONLY="#" ], [*openbsd*],[ - LIBS=`echo $LIBS | sed -e "s/-ldl//"` + # We need to explicitly link libc + LDFLAGS="$LDFLAGS -Wl,-lc" + # We also need to enable PIC + CFLAGS="-fPIC $CFLAGS" build_target="openbsd" - use_openbsd_libtool=true LIBPREFIX= DLLDIR=lib UNIXONLY="#" -- cgit v1.2.3 From 53cc51e0df6bcf1417ef058e0d5843c7f89933d5 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 18 Aug 2020 11:08:34 +0200 Subject: add missing header --- src/include/gnunet_setu_service.h | 378 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 378 insertions(+) create mode 100644 src/include/gnunet_setu_service.h diff --git a/src/include/gnunet_setu_service.h b/src/include/gnunet_setu_service.h new file mode 100644 index 000000000..092c03198 --- /dev/null +++ b/src/include/gnunet_setu_service.h @@ -0,0 +1,378 @@ +/* + This file is part of GNUnet + Copyright (C) 2013, 2014, 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Florian Dold + * @author Christian Grothoff + * + * @file + * Two-peer set operations + * + * @defgroup set Set service + * Two-peer set operations + * + * @{ + */ + +#ifndef GNUNET_SETU_SERVICE_H +#define GNUNET_SETU_SERVICE_H + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include "gnunet_common.h" +#include "gnunet_time_lib.h" +#include "gnunet_configuration_lib.h" + + +/** + * Maximum size of a context message for set operation requests. + */ +#define GNUNET_SETU_CONTEXT_MESSAGE_MAX_SIZE ((1 << 16) - 1024) + +/** + * Opaque handle to a set. + */ +struct GNUNET_SETU_Handle; + +/** + * Opaque handle to a set operation request from another peer. + */ +struct GNUNET_SETU_Request; + +/** + * Opaque handle to a listen operation. + */ +struct GNUNET_SETU_ListenHandle; + +/** + * Opaque handle to a set operation. + */ +struct GNUNET_SETU_OperationHandle; + + +/** + * Status for the result callback + */ +enum GNUNET_SETU_Status +{ + + /** + * Element should be added to the result set of the local peer, i.e. the + * local peer is missing an element. + */ + GNUNET_SETU_STATUS_ADD_LOCAL, + + /** + * The other peer refused to do the operation with us, or something went + * wrong. + */ + GNUNET_SETU_STATUS_FAILURE, + + /** + * Success, all elements have been sent (and received). + */ + GNUNET_SETU_STATUS_DONE +}; + + +/** + * Element stored in a set. + */ +struct GNUNET_SETU_Element +{ + /** + * Number of bytes in the buffer pointed to by data. + */ + uint16_t size; + + /** + * Application-specific element type. + */ + uint16_t element_type; + + /** + * Actual data of the element + */ + const void *data; +}; + + +/** + * Possible options to pass to a set operation. + * + * Used as tag for struct #GNUNET_SETU_Option. + */ +enum GNUNET_SETU_OptionType +{ + /** + * List terminator. + */ + GNUNET_SETU_OPTION_END=0, + + /** + * Fail set operations when the other peer shows weird behavior + * that might by a Byzantine fault. + * + * For set union, 'v.num' is a lower bound on elements that the other peer + * must have in common with us. + */ + GNUNET_SETU_OPTION_BYZANTINE=1, + + /** + * Do not use the optimized set operation, but send full sets. Might + * trigger Byzantine fault detection. + */ + GNUNET_SETU_OPTION_FORCE_FULL=2, + + /** + * Only use optimized set operations, even though for this particular set + * operation they might be much slower. Might trigger Byzantine fault + * detection. + */ + GNUNET_SETU_OPTION_FORCE_DELTA=4, +}; + + +/** + * Option for set operations. + */ +struct GNUNET_SETU_Option +{ + /** + * Type of the option. + */ + enum GNUNET_SETU_OptionType type; + + /** + * Value for the option, only used with some options. + */ + union + { + uint64_t num; + } v; +}; + + +/** + * Callback for set union operation results. Called for each element + * in the result set. + * + * @param cls closure + * @param element a result element, only valid if status is #GNUNET_SETU_STATUS_OK + * @param current_size current set size + * @param status see `enum GNUNET_SETU_Status` + */ +typedef void +(*GNUNET_SETU_ResultIterator) (void *cls, + const struct GNUNET_SETU_Element *element, + uint64_t current_size, + enum GNUNET_SETU_Status status); + + +/** + * Called when another peer wants to do a set operation with the + * local peer. If a listen error occurs, the @a request is NULL. + * + * @param cls closure + * @param other_peer the other peer + * @param context_msg message with application specific information from + * the other peer + * @param request request from the other peer (never NULL), use GNUNET_SETU_accept() + * to accept it, otherwise the request will be refused + * Note that we can't just return value from the listen callback, + * as it is also necessary to specify the set we want to do the + * operation with, whith sometimes can be derived from the context + * message. It's necessary to specify the timeout. + */ +typedef void +(*GNUNET_SETU_ListenCallback) (void *cls, + const struct GNUNET_PeerIdentity *other_peer, + const struct GNUNET_MessageHeader *context_msg, + struct GNUNET_SETU_Request *request); + + +/** + * Create an empty set, supporting the specified operation. + * + * @param cfg configuration to use for connecting to the + * set service + * @return a handle to the set + */ +struct GNUNET_SETU_Handle * +GNUNET_SETU_create (const struct GNUNET_CONFIGURATION_Handle *cfg); + + +/** + * Add an element to the given set. + * + * @param set set to add element to + * @param element element to add to the set + * @param cb function to call when finished, can be NULL + * @param cb_cls closure for @a cb + * @return #GNUNET_OK on success, #GNUNET_SYSERR if the + * set is invalid (e.g. the set service crashed) + */ +int +GNUNET_SETU_add_element (struct GNUNET_SETU_Handle *set, + const struct GNUNET_SETU_Element *element, + GNUNET_SCHEDULER_TaskCallback cb, + void *cb_cls); + + +/** + * Destroy the set handle, and free all associated resources. Operations may + * still be pending when a set is destroyed (and will be allowed to complete). + * + * @param set set to destroy + */ +void +GNUNET_SETU_destroy (struct GNUNET_SETU_Handle *set); + + +/** + * Prepare a set operation to be evaluated with another peer. The evaluation + * will not start until the client provides a local set with + * GNUNET_SETU_commit(). + * + * @param other_peer peer with the other set + * @param app_id hash for the application using the set + * @param context_msg additional information for the request + * @param options options to use when processing the request + * @param result_cb called on error or success + * @param result_cls closure for @a result_cb + * @return a handle to cancel the operation + */ +struct GNUNET_SETU_OperationHandle * +GNUNET_SETU_prepare (const struct GNUNET_PeerIdentity *other_peer, + const struct GNUNET_HashCode *app_id, + const struct GNUNET_MessageHeader *context_msg, + const struct GNUNET_SETU_Option options[], + GNUNET_SETU_ResultIterator result_cb, + void *result_cls); + + +/** + * Wait for set operation requests for the given application ID. + * If the connection to the set service is lost, the listener is + * re-created transparently with exponential backoff. + * + * @param cfg configuration to use for connecting to + * the set service + * @param app_id id of the application that handles set operation requests + * @param listen_cb called for each incoming request matching the operation + * and application id + * @param listen_cls handle for @a listen_cb + * @return a handle that can be used to cancel the listen operation + */ +struct GNUNET_SETU_ListenHandle * +GNUNET_SETU_listen (const struct GNUNET_CONFIGURATION_Handle *cfg, + const struct GNUNET_HashCode *app_id, + GNUNET_SETU_ListenCallback listen_cb, + void *listen_cls); + + +/** + * Cancel the given listen operation. After calling cancel, the + * listen callback for this listen handle will not be called again. + * Note that cancelling a listen operation will automatically reject + * all operations that have not yet been accepted. + * + * @param lh handle for the listen operation + */ +void +GNUNET_SETU_listen_cancel (struct GNUNET_SETU_ListenHandle *lh); + + +/** + * Accept a request we got via GNUNET_SETU_listen(). Must be called during + * GNUNET_SETU_listen(), as the `struct GNUNET_SETU_Request` becomes invalid + * afterwards. + * Call GNUNET_SETU_commit() to provide the local set to use for the operation, + * and to begin the exchange with the remote peer. + * + * @param request request to accept + * @param options options to use when processing the request + * @param result_cb callback for the results + * @param result_cls closure for @a result_cb + * @return a handle to cancel the operation + */ +struct GNUNET_SETU_OperationHandle * +GNUNET_SETU_accept (struct GNUNET_SETU_Request *request, + const struct GNUNET_SETU_Option options[], + GNUNET_SETU_ResultIterator result_cb, + void *result_cls); + + +/** + * Commit a set to be used with a set operation. + * This function is called once we have fully constructed + * the set that we want to use for the operation. At this + * time, the P2P protocol can then begin to exchange the + * set information and call the result callback with the + * result information. + * + * @param oh handle to the set operation + * @param set the set to use for the operation + * @return #GNUNET_OK on success, #GNUNET_SYSERR if the + * set is invalid (e.g. the set service crashed) + */ +int +GNUNET_SETU_commit (struct GNUNET_SETU_OperationHandle *oh, + struct GNUNET_SETU_Handle *set); + + +/** + * Cancel the given set operation. May not be called after the operation's + * `GNUNET_SETU_ResultIterator` has been called with a status of + * #GNUNET_SETU_STATUS_FAILURE or #GNUNET_SETU_STATUS_DONE. + * + * @param oh set operation to cancel + */ +void +GNUNET_SETU_operation_cancel (struct GNUNET_SETU_OperationHandle *oh); + + +/** + * Hash a set element. + * + * @param element the element that should be hashed + * @param[out] ret_hash a pointer to where the hash of @a element + * should be stored + */ +void +GNUNET_SETU_element_hash (const struct GNUNET_SETU_Element *element, + struct GNUNET_HashCode *ret_hash); + + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + +#endif + +/** @} */ /* end of group */ -- cgit v1.2.3 From 2cd052cb8e4d2f2c3e5e9f1ed2a9b58f6af069bb Mon Sep 17 00:00:00 2001 From: Martin Schanzenbach Date: Tue, 18 Aug 2020 13:35:24 +0200 Subject: Update reclaimID handbook entry --- doc/handbook/chapters/user.texi | 95 ++++++++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 35 deletions(-) diff --git a/doc/handbook/chapters/user.texi b/doc/handbook/chapters/user.texi index 523f286d0..adc287e04 100644 --- a/doc/handbook/chapters/user.texi +++ b/doc/handbook/chapters/user.texi @@ -1988,13 +1988,13 @@ as a guide. @node reclaimID Identity Provider @section reclaimID Identity Provider -The reclaimID Identity Provider (IdP) is a decentralized IdP service. +The re:claimID Identity Provider (IdP) is a decentralized IdP service. It allows its users to manage and authorize third parties to access their identity attributes such as email or shipping addresses. It basically mimics the concepts of centralized IdPs, such as those offered by Google or Facebook. -Like other IdPs, reclaimID features an (optional) OpenID-Connect +Like other IdPs, reclaimID features an (optional) OpenID Connect 1.0-compliant protocol layer that can be used for websites to integrate reclaimID as an Identity Provider with little effort. @@ -2038,38 +2038,45 @@ In the future there might be more value types such as X.509 certificate credenti If you want to allow a third party such as a website or friend to access to your attributes (or a subset thereof) execute: @example -$ gnunet-reclaim -e "user" -r "PKEY" -i "attribute1,attribute2,..." +$ TICKET=$(gnunet-reclaim -e "user" -r "$RP_KEY" -i "attribute1,attribute2,...") @end example -Where "PKEY" is the public key of the third party and "attribute1,attribute2,..." is a comma-separated list of attribute names, such as "email,name,...", that you want to share. - The command will return a "ticket" string. -You must give this "ticket" to the requesting third party. +You must give $TICKET to the requesting third party. + +$RP_KEY is the public key of the third party and "attribute1,attribute2,..." is a comma-separated list of attribute names, such as "email,name,...", that you want to share. + +The third party may retrieve the key in string format for use in the above +call using "gnunet-identity": + +@example +$ RP_KEY=$(gnunet-identity -d grep "relyingparty" | awk '@{print $3@}') +@end example The third party can then retrieve your shared identity attributes using: @example -$ gnunet-reclaim -e "friend" -C "ticket" +$ gnunet-reclaim -e "relyingparty" -C "ticket" @end example -Where "friend" is the name for "user" that the requesting party is using. +Where "relyingparty" is the name for the identity behind $RP_KEY that the +requesting party is using. This will retrieve and list the shared identity attributes. The above command will also work if the user is currently offline since the attributes are retrieved from GNS. -Further, the "ticket" can be re-used later to retrieve up-to-date attributes in case "friend" has changed the value(s). For instance, because his email address changed. +Further, $TICKET can be re-used later to retrieve up-to-date attributes in case "friend" has changed the value(s). For instance, because his email address changed. To list all given authorizations (tickets) you can execute: @example -$ gnunet-reclaim -e "friend" -T (TODO there is only a C and REST API for this at this time) +$ gnunet-reclaim -e "user" -T @end example - @node Revoking Authorizations of Third Parties @subsection Revoking Authorizations of Third Parties If you want to revoke the access of a third party to your attributes you can execute: @example -$ gnunet-reclaim -e "user" -R "ticket" +$ gnunet-reclaim -e "user" -R $TICKET @end example This will prevent the third party from accessing the attribute in the future. @@ -2080,30 +2087,26 @@ This behaviour is _exactly the same_ as with other IdPs. @node OpenID Connect @subsection OpenID Connect -There is an OpenID Connect API for use with reclaimID. +There is an @uref{OpenID Connect, https://openid.net/specs/openid-connect-core-1_0.html} API for use with re:claimID. However, its use is quite complicated to setup. -As a proof-of-concept, you can look at https://gitlab.com/reclaimid. - -In the PoC and by convention for reclaimID, the OpenID Connect Endpoints are -found at: @example -http://api.reclaim/openid/authorize -http://api.reclaim/openid/token -http://api.reclaim/openid/userinfo -http://api.reclaim/openid/login +https://api.reclaim/openid/authorize +http://localhost:7776/openid/token +http://localhost:7776/openid/userinfo +http://localhost:7776/openid/login @end example The token endpoint is protected using HTTP basic authentication. You can authenticate using any username and the password configured under: @example -$ gnunet-config -s reclaim-rest-plugin -o PSW +$ gnunet-config -s reclaim-rest-plugin -o OIDC_CLIENT_SECRET @end example The authorize endpoint is protected using a Cookie which can be obtained through a request against the login endpoint. -This flow is meant to be used in the context of the OpenID Connect authorization +This functionality is meant to be used in the context of the OpenID Connect authorization flow to collect user consent interactively. Without a Cookie, the authorize endpoint redirects to a URI configured under: @@ -2111,17 +2114,22 @@ Without a Cookie, the authorize endpoint redirects to a URI configured under: $ gnunet-config -s reclaim-rest-plugin -o ADDRESS @end example -Our PoC includes a user interface (https://gitlab.com/reclaimid) which -integrates this process is an OpenID Connect compatible fashion. - The token endpoint is protected using OAuth2 and expects the grant which is retrieved from the authorization endpoint according to the standard. The userinfo endpoint is protected using OAuth2 and expects a bearer access token which is retrieved from a token request. -In order to create and register a client you need to execute the following -steps: +In order to make use of OpenID Connect flows as a user, you need to install +the browser plugin: + +@itemize @bullet +@item @uref{https://addons.mozilla.org/addon/reclaimid/, Firefox Add-on} +@item @uref{https://chrome.google.com/webstore/detail/reclaimid/jiogompmdejcnacmlnjhnaicgkefcfll, Chrome Web Store} +@end itemize + +In order to create and register an OpenID Connect client as a relying party, +you need to execute the following steps: @example $ gnunet-identity -C @@ -2129,16 +2137,23 @@ $ gnunet-namestore -z -a -n "@@" -t RECLAIM_OIDC_REDIRECT -V -a -n "@@" -t RECLAIM_OIDC_CLIENT -V "My OIDC Client" -e 1d -p @end example -The client_id will be the public key of the client. -As a redirect URI, you may use any globally unique DNS or GNS URI. -The client description will be displayed to the user on authorization. +The "client_id" for use in OpenID Connect is the public key of the client as +displayed using: +@example +$ gnunet-identity -d grep "relyingparty" | awk '@{print $3@}' +@end example + +The RECLAIM_OIDC_REDIRECT record contains your website redirect URI. +You may use any globally unique DNS or GNS URI. +The RECLAIM_OIDC_CLIENT record represents the client description which whill +be displayed to users in an authorization request. -Any website or relying party must use the endpoint -https://api.reclaim/openid/authorize in its authorization redirects, e.g. +Any website or relying party must use the authorization endpoint +@uref{https://api.reclaim/openid/authorize} in its authorization redirects, e.g. @example Login @end example @@ -2146,7 +2161,17 @@ https://api.reclaim/openid/authorize in its authorization redirects, e.g. This will direct the user's browser onto his local reclaimID instance. After giving consent, you will be provided with the OpenID Connect authorization code according to the specifications at your provided redirect URI. -The example code for the PoC website can be found at https://gitlab.com/reclaimid/demo. + +The ID Tokens issues by the token endpoints are signed using HS512 with the +shared secret configured under: + +@example +$ gnunet-config -s reclaim-rest-plugin -o JWT_SECRET +@end example + +The authorization code flow optionally supports @uref{https://tools.ietf.org/html/rfc7636, Proof Key for Code Exchange}. +If PKCE is used, the client does not need to authenticate against the token +endpoint. @node Using the Virtual Public Network @section Using the Virtual Public Network -- cgit v1.2.3 From 88f2fcadb2814601dc18a94718f631619d3b21b2 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 18 Aug 2020 14:42:25 +0200 Subject: fix message types --- src/setu/gnunet-service-setu.c | 73 +++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/src/setu/gnunet-service-setu.c b/src/setu/gnunet-service-setu.c index 88edc622f..d889c1889 100644 --- a/src/setu/gnunet-service-setu.c +++ b/src/setu/gnunet-service-setu.c @@ -462,7 +462,7 @@ fail_union_operation (struct Operation *op) LOG (GNUNET_ERROR_TYPE_WARNING, "union operation failed\n"); - ev = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SET_RESULT); + ev = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SETU_RESULT); msg->result_status = htons (GNUNET_SETU_STATUS_FAILURE); msg->request_id = htonl (op->client_request_id); msg->element_type = htons (0); @@ -801,7 +801,7 @@ send_ibf (struct Operation *op, ev = GNUNET_MQ_msg_extra (msg, buckets_in_message * IBF_BUCKET_SIZE, - GNUNET_MESSAGE_TYPE_SET_UNION_P2P_IBF); + GNUNET_MESSAGE_TYPE_SETU_P2P_IBF); msg->reserved1 = 0; msg->reserved2 = 0; msg->order = ibf_order; @@ -872,7 +872,7 @@ send_full_element_iterator (void *cls, GNUNET_h2s (key)); ev = GNUNET_MQ_msg_extra (emsg, el->size, - GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_ELEMENT); + GNUNET_MESSAGE_TYPE_SETU_P2P_FULL_ELEMENT); emsg->element_type = htons (el->element_type); GNUNET_memcpy (&emsg[1], el->data, @@ -901,7 +901,7 @@ send_full_set (struct Operation *op) (void) GNUNET_CONTAINER_multihashmap_iterate (op->set->content->elements, &send_full_element_iterator, op); - ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_DONE); + ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SETU_P2P_FULL_DONE); GNUNET_MQ_send (op->mq, ev); } @@ -926,7 +926,7 @@ check_union_p2p_strata_estimator (void *cls, GNUNET_break (0); return GNUNET_SYSERR; } - is_compressed = (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SEC == htons ( + is_compressed = (GNUNET_MESSAGE_TYPE_SETU_P2P_SEC == htons ( msg->header.type)); len = ntohs (msg->header.size) - sizeof(struct StrataEstimatorMessage); if ((GNUNET_NO == is_compressed) && @@ -956,7 +956,7 @@ handle_union_p2p_strata_estimator (void *cls, size_t len; int is_compressed; - is_compressed = (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SEC == htons ( + is_compressed = (GNUNET_MESSAGE_TYPE_SETU_P2P_SEC == htons ( msg->header.type)); GNUNET_STATISTICS_update (_GSS_statistics, "# bytes of SE received", @@ -1045,7 +1045,7 @@ handle_union_p2p_strata_estimator (void *cls, "Telling other peer that we expect its full set\n"); op->state->phase = PHASE_EXPECT_IBF; ev = GNUNET_MQ_msg_header ( - GNUNET_MESSAGE_TYPE_SET_UNION_P2P_REQUEST_FULL); + GNUNET_MESSAGE_TYPE_SETU_P2P_REQUEST_FULL); GNUNET_MQ_send (op->mq, ev); } @@ -1095,7 +1095,7 @@ send_offers_iterator (void *cls, ev = GNUNET_MQ_msg_header_extra (mh, sizeof(struct GNUNET_HashCode), - GNUNET_MESSAGE_TYPE_SET_UNION_P2P_OFFER); + GNUNET_MESSAGE_TYPE_SETU_P2P_OFFER); GNUNET_assert (NULL != ev); *(struct GNUNET_HashCode *) &mh[1] = ke->element->element_hash; @@ -1246,7 +1246,7 @@ decode_and_send (struct Operation *op) LOG (GNUNET_ERROR_TYPE_DEBUG, "transmitted all values, sending DONE\n"); - ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DONE); + ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SETU_P2P_DONE); GNUNET_MQ_send (op->mq, ev); /* We now wait until we get a DONE message back * and then wait for our MQ to be flushed and all our @@ -1272,7 +1272,7 @@ decode_and_send (struct Operation *op) * the effort additional complexity. */ ev = GNUNET_MQ_msg_extra (msg, sizeof(struct IBF_Key), - GNUNET_MESSAGE_TYPE_SET_UNION_P2P_INQUIRY); + GNUNET_MESSAGE_TYPE_SETU_P2P_INQUIRY); msg->salt = htonl (op->state->salt_receive); GNUNET_memcpy (&msg[1], &key, @@ -1452,7 +1452,7 @@ send_client_element (struct Operation *op, GNUNET_assert (0 != op->client_request_id); ev = GNUNET_MQ_msg_extra (rm, element->size, - GNUNET_MESSAGE_TYPE_SET_RESULT); + GNUNET_MESSAGE_TYPE_SETU_RESULT); if (NULL == ev) { GNUNET_MQ_discard (ev); @@ -1498,7 +1498,7 @@ send_client_done (void *cls) "# Union operations failed", 1, GNUNET_NO); - ev = GNUNET_MQ_msg (rm, GNUNET_MESSAGE_TYPE_SET_RESULT); + ev = GNUNET_MQ_msg (rm, GNUNET_MESSAGE_TYPE_SETU_RESULT); rm->result_status = htons (GNUNET_SETU_STATUS_FAILURE); rm->request_id = htonl (op->client_request_id); rm->element_type = htons (0); @@ -1516,7 +1516,7 @@ send_client_done (void *cls) LOG (GNUNET_ERROR_TYPE_INFO, "Signalling client that union operation is done\n"); ev = GNUNET_MQ_msg (rm, - GNUNET_MESSAGE_TYPE_SET_RESULT); + GNUNET_MESSAGE_TYPE_SETU_RESULT); rm->request_id = htonl (op->client_request_id); rm->result_status = htons (GNUNET_SETU_STATUS_DONE); rm->element_type = htons (0); @@ -1550,7 +1550,7 @@ maybe_finish (struct Operation *op) struct GNUNET_MQ_Envelope *ev; op->state->phase = PHASE_DONE; - ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DONE); + ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SETU_P2P_DONE); GNUNET_MQ_send (op->mq, ev); /* We now wait until the other peer sends P2P_OVER @@ -1877,7 +1877,7 @@ send_missing_full_elements_iter (void *cls, return GNUNET_YES; ev = GNUNET_MQ_msg_extra (emsg, ee->element.size, - GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_ELEMENT); + GNUNET_MESSAGE_TYPE_SETU_P2P_FULL_ELEMENT); GNUNET_memcpy (&emsg[1], ee->element.data, ee->element.size); @@ -1942,7 +1942,7 @@ handle_union_p2p_full_done (void *cls, &send_missing_full_elements_iter, op); - ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_DONE); + ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SETU_P2P_FULL_DONE); GNUNET_MQ_send (op->mq, ev); op->state->phase = PHASE_DONE; @@ -2044,7 +2044,7 @@ handle_union_p2p_demand (void *cls, return; } ev = GNUNET_MQ_msg_extra (emsg, ee->element.size, - GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENTS); + GNUNET_MESSAGE_TYPE_SETU_P2P_ELEMENTS); GNUNET_memcpy (&emsg[1], ee->element.data, ee->element.size); emsg->reserved = htons (0); emsg->element_type = htons (ee->element.element_type); @@ -2148,7 +2148,7 @@ handle_union_p2p_offer (void *cls, (void *) op, GNUNET_h2s (hash)); ev = GNUNET_MQ_msg_header_extra (demands, sizeof(struct GNUNET_HashCode), - GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DEMAND); + GNUNET_MESSAGE_TYPE_SETU_P2P_DEMAND); GNUNET_memcpy (&demands[1], hash, sizeof(struct GNUNET_HashCode)); @@ -2242,7 +2242,7 @@ union_evaluate (struct Operation *op, struct OperationRequestMessage *msg; ev = GNUNET_MQ_msg_nested_mh (msg, - GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST, + GNUNET_MESSAGE_TYPE_SETU_P2P_OPERATION_REQUEST, opaque_context); if (NULL == ev) { @@ -2693,7 +2693,7 @@ handle_client_create_set (void *cls, * Timeout happens iff: * - we suggested an operation to our listener, * but did not receive a response in time - * - we got the channel from a peer but no #GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST + * - we got the channel from a peer but no #GNUNET_MESSAGE_TYPE_SETU_P2P_OPERATION_REQUEST * * @param cls channel context * @param tc context information (why was this task triggered now) @@ -3004,7 +3004,6 @@ handle_client_set_add (void *cls, } GNUNET_SERVICE_client_continue (cs->client); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Executing mutation on set\n"); - GNUNET_assert (GNUNET_MESSAGE_TYPE_SET_ADD == ntohs (msg->header.type)); el.size = ntohs (msg->header.size) - sizeof(*msg); el.data = &msg[1]; el.element_type = ntohs (msg->element_type); @@ -3096,55 +3095,55 @@ handle_client_evaluate (void *cls, struct Operation *op = GNUNET_new (struct Operation); const struct GNUNET_MQ_MessageHandler cadet_handlers[] = { GNUNET_MQ_hd_var_size (incoming_msg, - GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST, + GNUNET_MESSAGE_TYPE_SETU_P2P_OPERATION_REQUEST, struct OperationRequestMessage, op), GNUNET_MQ_hd_var_size (union_p2p_ibf, - GNUNET_MESSAGE_TYPE_SET_UNION_P2P_IBF, + GNUNET_MESSAGE_TYPE_SETU_P2P_IBF, struct IBFMessage, op), GNUNET_MQ_hd_var_size (union_p2p_elements, - GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENTS, + GNUNET_MESSAGE_TYPE_SETU_P2P_ELEMENTS, struct GNUNET_SETU_ElementMessage, op), GNUNET_MQ_hd_var_size (union_p2p_offer, - GNUNET_MESSAGE_TYPE_SET_UNION_P2P_OFFER, + GNUNET_MESSAGE_TYPE_SETU_P2P_OFFER, struct GNUNET_MessageHeader, op), GNUNET_MQ_hd_var_size (union_p2p_inquiry, - GNUNET_MESSAGE_TYPE_SET_UNION_P2P_INQUIRY, + GNUNET_MESSAGE_TYPE_SETU_P2P_INQUIRY, struct InquiryMessage, op), GNUNET_MQ_hd_var_size (union_p2p_demand, - GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DEMAND, + GNUNET_MESSAGE_TYPE_SETU_P2P_DEMAND, struct GNUNET_MessageHeader, op), GNUNET_MQ_hd_fixed_size (union_p2p_done, - GNUNET_MESSAGE_TYPE_SET_UNION_P2P_DONE, + GNUNET_MESSAGE_TYPE_SETU_P2P_DONE, struct GNUNET_MessageHeader, op), GNUNET_MQ_hd_fixed_size (union_p2p_over, - GNUNET_MESSAGE_TYPE_SET_UNION_P2P_OVER, + GNUNET_MESSAGE_TYPE_SETU_P2P_OVER, struct GNUNET_MessageHeader, op), GNUNET_MQ_hd_fixed_size (union_p2p_full_done, - GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_DONE, + GNUNET_MESSAGE_TYPE_SETU_P2P_FULL_DONE, struct GNUNET_MessageHeader, op), GNUNET_MQ_hd_fixed_size (union_p2p_request_full, - GNUNET_MESSAGE_TYPE_SET_UNION_P2P_REQUEST_FULL, + GNUNET_MESSAGE_TYPE_SETU_P2P_REQUEST_FULL, struct GNUNET_MessageHeader, op), GNUNET_MQ_hd_var_size (union_p2p_strata_estimator, - GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SE, + GNUNET_MESSAGE_TYPE_SETU_P2P_SE, struct StrataEstimatorMessage, op), GNUNET_MQ_hd_var_size (union_p2p_strata_estimator, - GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SEC, + GNUNET_MESSAGE_TYPE_SETU_P2P_SEC, struct StrataEstimatorMessage, op), GNUNET_MQ_hd_var_size (union_p2p_full_element, - GNUNET_MESSAGE_TYPE_SET_UNION_P2P_FULL_ELEMENT, + GNUNET_MESSAGE_TYPE_SETU_P2P_FULL_ELEMENT, struct GNUNET_SETU_ElementMessage, op), GNUNET_MQ_handler_end () @@ -3290,7 +3289,7 @@ handle_client_accept (void *cls, ntohl (msg->accept_reject_id), cs->listener); ev = GNUNET_MQ_msg (result_message, - GNUNET_MESSAGE_TYPE_SET_RESULT); + GNUNET_MESSAGE_TYPE_SETU_RESULT); result_message->request_id = msg->request_id; result_message->result_status = htons (GNUNET_SETU_STATUS_FAILURE); GNUNET_MQ_send (set->cs->mq, ev); @@ -3356,9 +3355,9 @@ handle_client_accept (void *cls, len = strata_estimator_write (se, buf); if (len < se->strata_count * IBF_BUCKET_SIZE * se->ibf_size) - type = GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SEC; + type = GNUNET_MESSAGE_TYPE_SETU_P2P_SEC; else - type = GNUNET_MESSAGE_TYPE_SET_UNION_P2P_SE; + type = GNUNET_MESSAGE_TYPE_SETU_P2P_SE; ev = GNUNET_MQ_msg_extra (strata_msg, len, type); -- cgit v1.2.3 From 48fdab91834c4649048d371d6b454e9b03b9fa50 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 18 Aug 2020 14:44:37 +0200 Subject: -indentation --- src/setu/gnunet-service-setu.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/setu/gnunet-service-setu.c b/src/setu/gnunet-service-setu.c index d889c1889..166522afa 100644 --- a/src/setu/gnunet-service-setu.c +++ b/src/setu/gnunet-service-setu.c @@ -561,7 +561,7 @@ op_get_element (struct Operation *op, ibf_key = get_ibf_key (element_hash); ret = GNUNET_CONTAINER_multihashmap32_get_multiple (op->state->key_to_element, (uint32_t) ibf_key.key_val, - op_get_element_iterator, + &op_get_element_iterator, &ctx); /* was the iteration aborted because we found the element? */ @@ -1649,7 +1649,8 @@ handle_union_p2p_elements (void *cls, op->state->received_total++; - ke = op_get_element (op, &ee->element_hash); + ke = op_get_element (op, + &ee->element_hash); if (NULL != ke) { /* Got repeated element. Should not happen since @@ -1745,7 +1746,8 @@ handle_union_p2p_full_element (void *cls, op->state->received_total++; - ke = op_get_element (op, &ee->element_hash); + ke = op_get_element (op, + &ee->element_hash); if (NULL != ke) { /* Got repeated element. Should not happen since @@ -1818,8 +1820,7 @@ check_union_p2p_inquiry (void *cls, /** - * Send offers (for GNUNET_Hash-es) in response - * to inquiries (for IBF_Key-s). + * Send offers (for GNUNET_Hash-es) in response to inquiries (for IBF_Key-s). * * @param cls the union operation * @param msg the message @@ -1853,8 +1854,8 @@ handle_union_p2p_inquiry (void *cls, /** - * Iterator over hash map entries, called to - * destroy the linked list of colliding ibf key entries. + * Iterator over hash map entries, called to destroy the linked list of + * colliding ibf key entries. * * @param cls closure * @param key current key code -- cgit v1.2.3 From ee6d0296795bb4cea9c0e157d6295a98d9e6d364 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 18 Aug 2020 15:05:30 +0200 Subject: -refactor to eliminate code no longer needed after set->setu specialization --- src/setu/gnunet-service-setu.c | 969 +++++++++++++++++++++++++---------------- src/setu/gnunet-service-setu.h | 393 ----------------- 2 files changed, 600 insertions(+), 762 deletions(-) delete mode 100644 src/setu/gnunet-service-setu.h diff --git a/src/setu/gnunet-service-setu.c b/src/setu/gnunet-service-setu.c index 166522afa..c59d375cf 100644 --- a/src/setu/gnunet-service-setu.c +++ b/src/setu/gnunet-service-setu.c @@ -26,15 +26,18 @@ #include "platform.h" #include "gnunet_util_lib.h" #include "gnunet_statistics_service.h" -#include "gnunet-service-setu.h" #include "ibf.h" +#include "gnunet_protocols.h" +#include "gnunet_applications.h" +#include "gnunet_cadet_service.h" #include "gnunet-service-setu_strata_estimator.h" #include "gnunet-service-setu_protocol.h" #include "gnunet_statistics_service.h" #include +#include "gnunet_setu_service.h" +#include "setu.h" - -#define LOG(kind, ...) GNUNET_log_from (kind, "set-union", __VA_ARGS__) +#define LOG(kind, ...) GNUNET_log_from (kind, "setu", __VA_ARGS__) /** * How long do we hold on to an incoming channel if there is @@ -142,10 +145,119 @@ enum UnionOperationPhase /** - * State of an evaluate operation with another peer. + * Information about an element element in the set. All elements are + * stored in a hash-table from their hash-code to their `struct + * Element`, so that the remove and add operations are reasonably + * fast. + */ +struct ElementEntry +{ + /** + * The actual element. The data for the element + * should be allocated at the end of this struct. + */ + struct GNUNET_SETU_Element element; + + /** + * Hash of the element. For set union: Will be used to derive the + * different IBF keys for different salts. + */ + struct GNUNET_HashCode element_hash; + + /** + * First generation that includes this element. + */ + unsigned int generation; + + /** + * #GNUNET_YES if the element is a remote element, and does not belong + * to the operation's set. + */ + int remote; +}; + + +/** + * A listener is inhabited by a client, and waits for evaluation + * requests from remote peers. + */ +struct Listener; + + +/** + * A set that supports a specific operation with other peers. + */ +struct Set; + + +/** + * State we keep per client. + */ +struct ClientState +{ + /** + * Set, if associated with the client, otherwise NULL. + */ + struct Set *set; + + /** + * Listener, if associated with the client, otherwise NULL. + */ + struct Listener *listener; + + /** + * Client handle. + */ + struct GNUNET_SERVICE_Client *client; + + /** + * Message queue. + */ + struct GNUNET_MQ_Handle *mq; +}; + + +/** + * Operation context used to execute a set operation. */ -struct OperationState +struct Operation { + /** + * Kept in a DLL of the listener, if @e listener is non-NULL. + */ + struct Operation *next; + + /** + * Kept in a DLL of the listener, if @e listener is non-NULL. + */ + struct Operation *prev; + + /** + * Channel to the peer. + */ + struct GNUNET_CADET_Channel *channel; + + /** + * Port this operation runs on. + */ + struct Listener *listener; + + /** + * Message queue for the channel. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Context message, may be NULL. + */ + struct GNUNET_MessageHeader *context_msg; + + /** + * Set associated with the operation, NULL until the spec has been + * associated with a set. + */ + struct Set *set; + /** * Copy of the set's strata estimator at the time of * creation of this operation. @@ -215,6 +327,152 @@ struct OperationState * the operation started. */ uint64_t initial_size; + + /** + * The identity of the requesting peer. Needs to + * be stored here as the op spec might not have been created yet. + */ + struct GNUNET_PeerIdentity peer; + + /** + * Timeout task, if the incoming peer has not been accepted + * after the timeout, it will be disconnected. + */ + struct GNUNET_SCHEDULER_Task *timeout_task; + + /** + * Salt to use for the operation. + */ + uint32_t salt; + + /** + * Remote peers element count + */ + uint32_t remote_element_count; + + /** + * ID used to identify an operation between service and client + */ + uint32_t client_request_id; + + /** + * Always use delta operation instead of sending full sets, + * even it it's less efficient. + */ + int force_delta; + + /** + * Always send full sets, even if delta operations would + * be more efficient. + */ + int force_full; + + /** + * #GNUNET_YES to fail operations where Byzantine faults + * are suspected + */ + int byzantine; + + /** + * Lower bound for the set size, used only when + * byzantine mode is enabled. + */ + int byzantine_lower_bound; + + /** + * Unique request id for the request from a remote peer, sent to the + * client, which will accept or reject the request. Set to '0' iff + * the request has not been suggested yet. + */ + uint32_t suggest_id; + + /** + * Generation in which the operation handle + * was created. + */ + unsigned int generation_created; +}; + + +/** + * SetContent stores the actual set elements, which may be shared by + * multiple generations derived from one set. + */ +struct SetContent +{ + /** + * Maps `struct GNUNET_HashCode *` to `struct ElementEntry *`. + */ + struct GNUNET_CONTAINER_MultiHashMap *elements; + + /** + * Number of references to the content. + */ + unsigned int refcount; + + /** + * FIXME: document! + */ + unsigned int latest_generation; + + /** + * Number of concurrently active iterators. + */ + int iterator_count; +}; + + +/** + * A set that supports a specific operation with other peers. + */ +struct Set +{ + /** + * Sets are held in a doubly linked list (in `sets_head` and `sets_tail`). + */ + struct Set *next; + + /** + * Sets are held in a doubly linked list. + */ + struct Set *prev; + + /** + * Client that owns the set. Only one client may own a set, + * and there can only be one set per client. + */ + struct ClientState *cs; + + /** + * Content, possibly shared by multiple sets, + * and thus reference counted. + */ + struct SetContent *content; + + /** + * The strata estimator is only generated once for + * each set. + * The IBF keys are derived from the element hashes with + * salt=0. + */ + struct StrataEstimator *se; + + /** + * Evaluate operations are held in a linked list. + */ + struct Operation *ops_head; + + /** + * Evaluate operations are held in a linked list. + */ + struct Operation *ops_tail; + + /** + * Current generation, that is, number of previously executed + * operations and lazy copies on the underlying set content. + */ + unsigned int current_generation; + }; @@ -266,21 +524,6 @@ struct SendElementClosure }; -/** - * Extra state required for efficient set union. - */ -struct SetState -{ - /** - * The strata estimator is only generated once for - * each set. - * The IBF keys are derived from the element hashes with - * salt=0. - */ - struct StrataEstimator *se; -}; - - /** * A listener is inhabited by a client, and waits for evaluation * requests from remote peers. @@ -340,7 +583,7 @@ static struct GNUNET_CADET_Handle *cadet; /** * Statistics handle. */ -struct GNUNET_STATISTICS_Handle *_GSS_statistics; +static struct GNUNET_STATISTICS_Handle *_GSS_statistics; /** * Listeners are held in a doubly linked list. @@ -401,50 +644,216 @@ destroy_key_to_element_iter (void *cls, /** - * Destroy the union operation. Only things specific to the union - * operation are destroyed. + * Destroy the union operation. Only things specific to the union + * operation are destroyed. + * + * @param op union operation to destroy + */ +static void +union_op_cancel (struct Operation *op) +{ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "destroying union op\n"); + /* check if the op was canceled twice */ + if (NULL != op->remote_ibf) + { + ibf_destroy (op->remote_ibf); + op->remote_ibf = NULL; + } + if (NULL != op->demanded_hashes) + { + GNUNET_CONTAINER_multihashmap_destroy (op->demanded_hashes); + op->demanded_hashes = NULL; + } + if (NULL != op->local_ibf) + { + ibf_destroy (op->local_ibf); + op->local_ibf = NULL; + } + if (NULL != op->se) + { + strata_estimator_destroy (op->se); + op->se = NULL; + } + if (NULL != op->key_to_element) + { + GNUNET_CONTAINER_multihashmap32_iterate (op->key_to_element, + &destroy_key_to_element_iter, + NULL); + GNUNET_CONTAINER_multihashmap32_destroy (op->key_to_element); + op->key_to_element = NULL; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "destroying union op done\n"); +} + + +/** + * Signal to the client that the operation has finished and + * destroy the operation. + * + * @param cls operation to destroy + */ +static void +send_client_done (void *cls) +{ + struct Operation *op = cls; + struct GNUNET_MQ_Envelope *ev; + struct GNUNET_SETU_ResultMessage *rm; + + if (GNUNET_YES == op->client_done_sent) + return; + if (PHASE_DONE != op->phase) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + "Union operation failed\n"); + GNUNET_STATISTICS_update (_GSS_statistics, + "# Union operations failed", + 1, + GNUNET_NO); + ev = GNUNET_MQ_msg (rm, GNUNET_MESSAGE_TYPE_SETU_RESULT); + rm->result_status = htons (GNUNET_SETU_STATUS_FAILURE); + rm->request_id = htonl (op->client_request_id); + rm->element_type = htons (0); + GNUNET_MQ_send (op->set->cs->mq, + ev); + return; + } + + op->client_done_sent = GNUNET_YES; + + GNUNET_STATISTICS_update (_GSS_statistics, + "# Union operations succeeded", + 1, + GNUNET_NO); + LOG (GNUNET_ERROR_TYPE_INFO, + "Signalling client that union operation is done\n"); + ev = GNUNET_MQ_msg (rm, + GNUNET_MESSAGE_TYPE_SETU_RESULT); + rm->request_id = htonl (op->client_request_id); + rm->result_status = htons (GNUNET_SETU_STATUS_DONE); + rm->element_type = htons (0); + rm->current_size = GNUNET_htonll (GNUNET_CONTAINER_multihashmap32_size ( + op->key_to_element)); + GNUNET_MQ_send (op->set->cs->mq, + ev); +} + + +/* FIXME: the destroy logic is a mess and should be cleaned up! */ + +/** + * Destroy the given operation. Used for any operation where both + * peers were known and that thus actually had a vt and channel. Must + * not be used for operations where 'listener' is still set and we do + * not know the other peer. + * + * Call the implementation-specific cancel function of the operation. + * Disconnects from the remote peer. Does not disconnect the client, + * as there may be multiple operations per set. + * + * @param op operation to destroy + */ +static void +_GSS_operation_destroy (struct Operation *op) +{ + struct Set *set = op->set; + struct GNUNET_CADET_Channel *channel; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Destroying operation %p\n", + op); + GNUNET_assert (NULL == op->listener); + // FIXME: inline? + union_op_cancel (op); + if (NULL != set) + { + GNUNET_CONTAINER_DLL_remove (set->ops_head, + set->ops_tail, + op); + op->set = NULL; + } + if (NULL != op->context_msg) + { + GNUNET_free (op->context_msg); + op->context_msg = NULL; + } + if (NULL != (channel = op->channel)) + { + /* This will free op; called conditionally as this helper function + is also called from within the channel disconnect handler. */ + op->channel = NULL; + GNUNET_CADET_channel_destroy (channel); + } + /* We rely on the channel end handler to free 'op'. When 'op->channel' was NULL, + * there was a channel end handler that will free 'op' on the call stack. */ +} + + +/** + * This function probably should not exist + * and be replaced by inlining more specific + * logic in the various places where it is called. + */ +static void +_GSS_operation_destroy2 (struct Operation *op); + + +/** + * Destroy an incoming request from a remote peer * - * @param op union operation to destroy + * @param op remote request to destroy */ static void -union_op_cancel (struct Operation *op) +incoming_destroy (struct Operation *op) { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "destroying union op\n"); - /* check if the op was canceled twice */ - GNUNET_assert (NULL != op->state); - if (NULL != op->state->remote_ibf) - { - ibf_destroy (op->state->remote_ibf); - op->state->remote_ibf = NULL; - } - if (NULL != op->state->demanded_hashes) + struct Listener *listener; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Destroying incoming operation %p\n", + op); + if (NULL != (listener = op->listener)) { - GNUNET_CONTAINER_multihashmap_destroy (op->state->demanded_hashes); - op->state->demanded_hashes = NULL; + GNUNET_CONTAINER_DLL_remove (listener->op_head, + listener->op_tail, + op); + op->listener = NULL; } - if (NULL != op->state->local_ibf) + if (NULL != op->timeout_task) { - ibf_destroy (op->state->local_ibf); - op->state->local_ibf = NULL; + GNUNET_SCHEDULER_cancel (op->timeout_task); + op->timeout_task = NULL; } - if (NULL != op->state->se) + _GSS_operation_destroy2 (op); +} + + +/** + * This function probably should not exist + * and be replaced by inlining more specific + * logic in the various places where it is called. + */ +static void +_GSS_operation_destroy2 (struct Operation *op) +{ + struct GNUNET_CADET_Channel *channel; + + if (NULL != (channel = op->channel)) { - strata_estimator_destroy (op->state->se); - op->state->se = NULL; + /* This will free op; called conditionally as this helper function + is also called from within the channel disconnect handler. */ + op->channel = NULL; + GNUNET_CADET_channel_destroy (channel); } - if (NULL != op->state->key_to_element) + if (NULL != op->listener) { - GNUNET_CONTAINER_multihashmap32_iterate (op->state->key_to_element, - &destroy_key_to_element_iter, - NULL); - GNUNET_CONTAINER_multihashmap32_destroy (op->state->key_to_element); - op->state->key_to_element = NULL; + incoming_destroy (op); + return; } - GNUNET_free (op->state); - op->state = NULL; - LOG (GNUNET_ERROR_TYPE_DEBUG, - "destroying union op done\n"); + if (NULL != op->set) + send_client_done (op); + _GSS_operation_destroy (op); + GNUNET_free (op); } @@ -559,7 +968,7 @@ op_get_element (struct Operation *op, ctx.hash = *element_hash; ibf_key = get_ibf_key (element_hash); - ret = GNUNET_CONTAINER_multihashmap32_get_multiple (op->state->key_to_element, + ret = GNUNET_CONTAINER_multihashmap32_get_multiple (op->key_to_element, (uint32_t) ibf_key.key_val, &op_get_element_iterator, &ctx); @@ -602,7 +1011,7 @@ op_register_element (struct Operation *op, k->ibf_key = ibf_key; k->received = received; GNUNET_assert (GNUNET_OK == - GNUNET_CONTAINER_multihashmap32_put (op->state->key_to_element, + GNUNET_CONTAINER_multihashmap32_put (op->key_to_element, (uint32_t) ibf_key.key_val, k, GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)); @@ -664,13 +1073,28 @@ prepare_ibf_iterator (void *cls, (unsigned long) ke->ibf_key.key_val, GNUNET_h2s (&ke->element->element_hash)); salt_key (&ke->ibf_key, - op->state->salt_send, + op->salt_send, &salted_key); - ibf_insert (op->state->local_ibf, salted_key); + ibf_insert (op->local_ibf, salted_key); return GNUNET_YES; } +/** + * Is element @a ee part of the set used by @a op? + * + * @param ee element to test + * @param op operation the defines the set and its generation + * @return #GNUNET_YES if the element is in the set, #GNUNET_NO if not + */ +static int +_GSS_is_element_of_operation (struct ElementEntry *ee, + struct Operation *op) +{ + return ee->generation >= op->generation_created; +} + + /** * Iterator for initializing the * key-to-element mapping of a union operation @@ -714,9 +1138,9 @@ initialize_key_to_element (struct Operation *op) { unsigned int len; - GNUNET_assert (NULL == op->state->key_to_element); + GNUNET_assert (NULL == op->key_to_element); len = GNUNET_CONTAINER_multihashmap_size (op->set->content->elements); - op->state->key_to_element = GNUNET_CONTAINER_multihashmap32_create (len + 1); + op->key_to_element = GNUNET_CONTAINER_multihashmap32_create (len + 1); GNUNET_CONTAINER_multihashmap_iterate (op->set->content->elements, &init_key_to_element_iterator, op); @@ -735,18 +1159,18 @@ static int prepare_ibf (struct Operation *op, uint32_t size) { - GNUNET_assert (NULL != op->state->key_to_element); + GNUNET_assert (NULL != op->key_to_element); - if (NULL != op->state->local_ibf) - ibf_destroy (op->state->local_ibf); - op->state->local_ibf = ibf_create (size, SE_IBF_HASH_NUM); - if (NULL == op->state->local_ibf) + if (NULL != op->local_ibf) + ibf_destroy (op->local_ibf); + op->local_ibf = ibf_create (size, SE_IBF_HASH_NUM); + if (NULL == op->local_ibf) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to allocate local IBF\n"); return GNUNET_SYSERR; } - GNUNET_CONTAINER_multihashmap32_iterate (op->state->key_to_element, + GNUNET_CONTAINER_multihashmap32_iterate (op->key_to_element, &prepare_ibf_iterator, op); return GNUNET_OK; @@ -786,7 +1210,7 @@ send_ibf (struct Operation *op, GNUNET_STATISTICS_update (_GSS_statistics, name, 1, GNUNET_NO); } - ibf = op->state->local_ibf; + ibf = op->local_ibf; while (buckets_sent < (1 << ibf_order)) { @@ -806,7 +1230,7 @@ send_ibf (struct Operation *op, msg->reserved2 = 0; msg->order = ibf_order; msg->offset = htonl (buckets_sent); - msg->salt = htonl (op->state->salt_send); + msg->salt = htonl (op->salt_send); ibf_write_slice (ibf, buckets_sent, buckets_in_message, &msg[1]); buckets_sent += buckets_in_message; @@ -820,7 +1244,7 @@ send_ibf (struct Operation *op, /* The other peer must decode the IBF, so * we're passive. */ - op->state->phase = PHASE_INVENTORY_PASSIVE; + op->phase = PHASE_INVENTORY_PASSIVE; return GNUNET_OK; } @@ -893,7 +1317,7 @@ send_full_set (struct Operation *op) { struct GNUNET_MQ_Envelope *ev; - op->state->phase = PHASE_FULL_SENDING; + op->phase = PHASE_FULL_SENDING; LOG (GNUNET_ERROR_TYPE_DEBUG, "Dedicing to transmit the full set\n"); /* FIXME: use a more memory-friendly way of doing this with an @@ -921,7 +1345,7 @@ check_union_p2p_strata_estimator (void *cls, int is_compressed; size_t len; - if (op->state->phase != PHASE_EXPECT_SE) + if (op->phase != PHASE_EXPECT_SE) { GNUNET_break (0); return GNUNET_SYSERR; @@ -984,16 +1408,16 @@ handle_union_p2p_strata_estimator (void *cls, fail_union_operation (op); return; } - GNUNET_assert (NULL != op->state->se); + GNUNET_assert (NULL != op->se); diff = strata_estimator_difference (remote_se, - op->state->se); + op->se); if (diff > 200) diff = diff * 3 / 2; strata_estimator_destroy (remote_se); - strata_estimator_destroy (op->state->se); - op->state->se = NULL; + strata_estimator_destroy (op->se); + op->se = NULL; LOG (GNUNET_ERROR_TYPE_DEBUG, "got se diff=%d, using ibf size %d\n", diff, @@ -1021,18 +1445,18 @@ handle_union_p2p_strata_estimator (void *cls, } if ((GNUNET_YES == op->force_full) || - (diff > op->state->initial_size / 4) || + (diff > op->initial_size / 4) || (0 == other_size)) { LOG (GNUNET_ERROR_TYPE_DEBUG, "Deciding to go for full set transmission (diff=%d, own set=%u)\n", diff, - op->state->initial_size); + op->initial_size); GNUNET_STATISTICS_update (_GSS_statistics, "# of full sends", 1, GNUNET_NO); - if ((op->state->initial_size <= other_size) || + if ((op->initial_size <= other_size) || (0 == other_size)) { send_full_set (op); @@ -1043,7 +1467,7 @@ handle_union_p2p_strata_estimator (void *cls, LOG (GNUNET_ERROR_TYPE_DEBUG, "Telling other peer that we expect its full set\n"); - op->state->phase = PHASE_EXPECT_IBF; + op->phase = PHASE_EXPECT_IBF; ev = GNUNET_MQ_msg_header ( GNUNET_MESSAGE_TYPE_SETU_P2P_REQUEST_FULL); GNUNET_MQ_send (op->mq, @@ -1123,7 +1547,7 @@ send_offers_for_key (struct Operation *op, send_cls.ibf_key = ibf_key; send_cls.op = op; (void) GNUNET_CONTAINER_multihashmap32_get_multiple ( - op->state->key_to_element, + op->key_to_element, (uint32_t) ibf_key. key_val, &send_offers_iterator, @@ -1147,22 +1571,22 @@ decode_and_send (struct Operation *op) unsigned int num_decoded; struct InvertibleBloomFilter *diff_ibf; - GNUNET_assert (PHASE_INVENTORY_ACTIVE == op->state->phase); + GNUNET_assert (PHASE_INVENTORY_ACTIVE == op->phase); if (GNUNET_OK != prepare_ibf (op, - op->state->remote_ibf->size)) + op->remote_ibf->size)) { GNUNET_break (0); /* allocation failed */ return GNUNET_SYSERR; } - diff_ibf = ibf_dup (op->state->local_ibf); + diff_ibf = ibf_dup (op->local_ibf); ibf_subtract (diff_ibf, - op->state->remote_ibf); + op->remote_ibf); - ibf_destroy (op->state->remote_ibf); - op->state->remote_ibf = NULL; + ibf_destroy (op->remote_ibf); + op->remote_ibf = NULL; LOG (GNUNET_ERROR_TYPE_DEBUG, "decoding IBF (size=%u)\n", @@ -1213,7 +1637,7 @@ decode_and_send (struct Operation *op) "# of IBF retries", 1, GNUNET_NO); - op->state->salt_send++; + op->salt_send++; if (GNUNET_OK != send_ibf (op, next_order)) { @@ -1258,7 +1682,7 @@ decode_and_send (struct Operation *op) struct IBF_Key unsalted_key; unsalt_key (&key, - op->state->salt_receive, + op->salt_receive, &unsalted_key); send_offers_for_key (op, unsalted_key); @@ -1273,7 +1697,7 @@ decode_and_send (struct Operation *op) ev = GNUNET_MQ_msg_extra (msg, sizeof(struct IBF_Key), GNUNET_MESSAGE_TYPE_SETU_P2P_INQUIRY); - msg->salt = htonl (op->state->salt_receive); + msg->salt = htonl (op->salt_receive); GNUNET_memcpy (&msg[1], &key, sizeof(struct IBF_Key)); @@ -1322,26 +1746,26 @@ check_union_p2p_ibf (void *cls, GNUNET_break_op (0); return GNUNET_SYSERR; } - if (op->state->phase == PHASE_EXPECT_IBF_CONT) + if (op->phase == PHASE_EXPECT_IBF_CONT) { - if (ntohl (msg->offset) != op->state->ibf_buckets_received) + if (ntohl (msg->offset) != op->ibf_buckets_received) { GNUNET_break_op (0); return GNUNET_SYSERR; } - if (1 << msg->order != op->state->remote_ibf->size) + if (1 << msg->order != op->remote_ibf->size) { GNUNET_break_op (0); return GNUNET_SYSERR; } - if (ntohl (msg->salt) != op->state->salt_receive) + if (ntohl (msg->salt) != op->salt_receive) { GNUNET_break_op (0); return GNUNET_SYSERR; } } - else if ((op->state->phase != PHASE_INVENTORY_PASSIVE) && - (op->state->phase != PHASE_EXPECT_IBF)) + else if ((op->phase != PHASE_INVENTORY_PASSIVE) && + (op->phase != PHASE_EXPECT_IBF)) { GNUNET_break_op (0); return GNUNET_SYSERR; @@ -1369,27 +1793,27 @@ handle_union_p2p_ibf (void *cls, buckets_in_message = (ntohs (msg->header.size) - sizeof *msg) / IBF_BUCKET_SIZE; - if ((op->state->phase == PHASE_INVENTORY_PASSIVE) || - (op->state->phase == PHASE_EXPECT_IBF)) + if ((op->phase == PHASE_INVENTORY_PASSIVE) || + (op->phase == PHASE_EXPECT_IBF)) { - op->state->phase = PHASE_EXPECT_IBF_CONT; - GNUNET_assert (NULL == op->state->remote_ibf); + op->phase = PHASE_EXPECT_IBF_CONT; + GNUNET_assert (NULL == op->remote_ibf); LOG (GNUNET_ERROR_TYPE_DEBUG, "Creating new ibf of size %u\n", 1 << msg->order); - op->state->remote_ibf = ibf_create (1 << msg->order, SE_IBF_HASH_NUM); - op->state->salt_receive = ntohl (msg->salt); + op->remote_ibf = ibf_create (1 << msg->order, SE_IBF_HASH_NUM); + op->salt_receive = ntohl (msg->salt); LOG (GNUNET_ERROR_TYPE_DEBUG, "Receiving new IBF with salt %u\n", - op->state->salt_receive); - if (NULL == op->state->remote_ibf) + op->salt_receive); + if (NULL == op->remote_ibf) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to parse remote IBF, closing connection\n"); fail_union_operation (op); return; } - op->state->ibf_buckets_received = 0; + op->ibf_buckets_received = 0; if (0 != ntohl (msg->offset)) { GNUNET_break_op (0); @@ -1399,23 +1823,23 @@ handle_union_p2p_ibf (void *cls, } else { - GNUNET_assert (op->state->phase == PHASE_EXPECT_IBF_CONT); + GNUNET_assert (op->phase == PHASE_EXPECT_IBF_CONT); LOG (GNUNET_ERROR_TYPE_DEBUG, "Received more of IBF\n"); } - GNUNET_assert (NULL != op->state->remote_ibf); + GNUNET_assert (NULL != op->remote_ibf); ibf_read_slice (&msg[1], - op->state->ibf_buckets_received, + op->ibf_buckets_received, buckets_in_message, - op->state->remote_ibf); - op->state->ibf_buckets_received += buckets_in_message; + op->remote_ibf); + op->ibf_buckets_received += buckets_in_message; - if (op->state->ibf_buckets_received == op->state->remote_ibf->size) + if (op->ibf_buckets_received == op->remote_ibf->size) { LOG (GNUNET_ERROR_TYPE_DEBUG, "received full ibf\n"); - op->state->phase = PHASE_INVENTORY_ACTIVE; + op->phase = PHASE_INVENTORY_ACTIVE; if (GNUNET_OK != decode_and_send (op)) { @@ -1463,7 +1887,7 @@ send_client_element (struct Operation *op, rm->request_id = htonl (op->client_request_id); rm->element_type = htons (element->element_type); rm->current_size = GNUNET_htonll (GNUNET_CONTAINER_multihashmap32_size ( - op->state->key_to_element)); + op->key_to_element)); GNUNET_memcpy (&rm[1], element->data, element->size); @@ -1472,61 +1896,6 @@ send_client_element (struct Operation *op, } -/** - * Signal to the client that the operation has finished and - * destroy the operation. - * - * @param cls operation to destroy - */ -static void -send_client_done (void *cls) -{ - struct Operation *op = cls; - struct GNUNET_MQ_Envelope *ev; - struct GNUNET_SETU_ResultMessage *rm; - - if (GNUNET_YES == op->state->client_done_sent) - { - return; - } - - if (PHASE_DONE != op->state->phase) - { - LOG (GNUNET_ERROR_TYPE_WARNING, - "Union operation failed\n"); - GNUNET_STATISTICS_update (_GSS_statistics, - "# Union operations failed", - 1, - GNUNET_NO); - ev = GNUNET_MQ_msg (rm, GNUNET_MESSAGE_TYPE_SETU_RESULT); - rm->result_status = htons (GNUNET_SETU_STATUS_FAILURE); - rm->request_id = htonl (op->client_request_id); - rm->element_type = htons (0); - GNUNET_MQ_send (op->set->cs->mq, - ev); - return; - } - - op->state->client_done_sent = GNUNET_YES; - - GNUNET_STATISTICS_update (_GSS_statistics, - "# Union operations succeeded", - 1, - GNUNET_NO); - LOG (GNUNET_ERROR_TYPE_INFO, - "Signalling client that union operation is done\n"); - ev = GNUNET_MQ_msg (rm, - GNUNET_MESSAGE_TYPE_SETU_RESULT); - rm->request_id = htonl (op->client_request_id); - rm->result_status = htons (GNUNET_SETU_STATUS_DONE); - rm->element_type = htons (0); - rm->current_size = GNUNET_htonll (GNUNET_CONTAINER_multihashmap32_size ( - op->state->key_to_element)); - GNUNET_MQ_send (op->set->cs->mq, - ev); -} - - /** * Tests if the operation is finished, and if so notify. * @@ -1538,9 +1907,9 @@ maybe_finish (struct Operation *op) unsigned int num_demanded; num_demanded = GNUNET_CONTAINER_multihashmap_size ( - op->state->demanded_hashes); + op->demanded_hashes); - if (PHASE_FINISH_WAITING == op->state->phase) + if (PHASE_FINISH_WAITING == op->phase) { LOG (GNUNET_ERROR_TYPE_DEBUG, "In PHASE_FINISH_WAITING, pending %u demands\n", @@ -1549,7 +1918,7 @@ maybe_finish (struct Operation *op) { struct GNUNET_MQ_Envelope *ev; - op->state->phase = PHASE_DONE; + op->phase = PHASE_DONE; ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SETU_P2P_DONE); GNUNET_MQ_send (op->mq, ev); @@ -1557,14 +1926,14 @@ maybe_finish (struct Operation *op) * after it got all elements from us. */ } } - if (PHASE_FINISH_CLOSING == op->state->phase) + if (PHASE_FINISH_CLOSING == op->phase) { LOG (GNUNET_ERROR_TYPE_DEBUG, "In PHASE_FINISH_CLOSING, pending %u demands\n", num_demanded); if (0 == num_demanded) { - op->state->phase = PHASE_DONE; + op->phase = PHASE_DONE; send_client_done (op); _GSS_operation_destroy2 (op); } @@ -1584,7 +1953,7 @@ check_union_p2p_elements (void *cls, { struct Operation *op = cls; - if (0 == GNUNET_CONTAINER_multihashmap_size (op->state->demanded_hashes)) + if (0 == GNUNET_CONTAINER_multihashmap_size (op->demanded_hashes)) { GNUNET_break_op (0); return GNUNET_SYSERR; @@ -1623,7 +1992,7 @@ handle_union_p2p_elements (void *cls, GNUNET_SETU_element_hash (&ee->element, &ee->element_hash); if (GNUNET_NO == - GNUNET_CONTAINER_multihashmap_remove (op->state->demanded_hashes, + GNUNET_CONTAINER_multihashmap_remove (op->demanded_hashes, &ee->element_hash, NULL)) { @@ -1647,7 +2016,7 @@ handle_union_p2p_elements (void *cls, 1, GNUNET_NO); - op->state->received_total++; + op->received_total++; ke = op_get_element (op, &ee->element_hash); @@ -1666,7 +2035,7 @@ handle_union_p2p_elements (void *cls, { LOG (GNUNET_ERROR_TYPE_DEBUG, "Registering new element from remote peer\n"); - op->state->received_fresh++; + op->received_fresh++; op_register_element (op, ee, GNUNET_YES); /* only send results immediately if the client wants it */ send_client_element (op, @@ -1674,8 +2043,8 @@ handle_union_p2p_elements (void *cls, GNUNET_SETU_STATUS_ADD_LOCAL); } - if ((op->state->received_total > 8) && - (op->state->received_fresh < op->state->received_total / 3)) + if ((op->received_total > 8) && + (op->received_fresh < op->received_total / 3)) { /* The other peer gave us lots of old elements, there's something wrong. */ GNUNET_break_op (0); @@ -1744,7 +2113,7 @@ handle_union_p2p_full_element (void *cls, 1, GNUNET_NO); - op->state->received_total++; + op->received_total++; ke = op_get_element (op, &ee->element_hash); @@ -1763,7 +2132,7 @@ handle_union_p2p_full_element (void *cls, { LOG (GNUNET_ERROR_TYPE_DEBUG, "Registering new element from remote peer\n"); - op->state->received_fresh++; + op->received_fresh++; op_register_element (op, ee, GNUNET_YES); /* only send results immediately if the client wants it */ send_client_element (op, @@ -1772,14 +2141,14 @@ handle_union_p2p_full_element (void *cls, } if ((GNUNET_YES == op->byzantine) && - (op->state->received_total > 384 + op->state->received_fresh * 4) && - (op->state->received_fresh < op->state->received_total / 6)) + (op->received_total > 384 + op->received_fresh * 4) && + (op->received_fresh < op->received_total / 6)) { /* The other peer gave us lots of old elements, there's something wrong. */ LOG (GNUNET_ERROR_TYPE_ERROR, "Other peer sent only %llu/%llu fresh elements, failing operation\n", - (unsigned long long) op->state->received_fresh, - (unsigned long long) op->state->received_total); + (unsigned long long) op->received_fresh, + (unsigned long long) op->received_total); GNUNET_break_op (0); fail_union_operation (op); return; @@ -1802,7 +2171,7 @@ check_union_p2p_inquiry (void *cls, struct Operation *op = cls; unsigned int num_keys; - if (op->state->phase != PHASE_INVENTORY_PASSIVE) + if (op->phase != PHASE_INVENTORY_PASSIVE) { GNUNET_break_op (0); return GNUNET_SYSERR; @@ -1903,7 +2272,7 @@ handle_union_p2p_request_full (void *cls, LOG (GNUNET_ERROR_TYPE_DEBUG, "Received request for full set transmission\n"); - if (PHASE_EXPECT_IBF != op->state->phase) + if (PHASE_EXPECT_IBF != op->phase) { GNUNET_break_op (0); fail_union_operation (op); @@ -1929,7 +2298,7 @@ handle_union_p2p_full_done (void *cls, { struct Operation *op = cls; - switch (op->state->phase) + switch (op->phase) { case PHASE_EXPECT_IBF: { @@ -1939,14 +2308,14 @@ handle_union_p2p_full_done (void *cls, "got FULL DONE, sending elements that other peer is missing\n"); /* send all the elements that did not come from the remote peer */ - GNUNET_CONTAINER_multihashmap32_iterate (op->state->key_to_element, + GNUNET_CONTAINER_multihashmap32_iterate (op->key_to_element, &send_missing_full_elements_iter, op); ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SETU_P2P_FULL_DONE); GNUNET_MQ_send (op->mq, ev); - op->state->phase = PHASE_DONE; + op->phase = PHASE_DONE; /* we now wait until the other peer sends us the OVER message*/ } break; @@ -1956,7 +2325,7 @@ handle_union_p2p_full_done (void *cls, LOG (GNUNET_ERROR_TYPE_DEBUG, "got FULL DONE, finishing\n"); /* We sent the full set, and got the response for that. We're done. */ - op->state->phase = PHASE_DONE; + op->phase = PHASE_DONE; GNUNET_CADET_receive_done (op->channel); send_client_done (op); _GSS_operation_destroy2 (op); @@ -1967,7 +2336,7 @@ handle_union_p2p_full_done (void *cls, default: GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Handle full done phase is %u\n", - (unsigned) op->state->phase); + (unsigned) op->phase); GNUNET_break_op (0); fail_union_operation (op); return; @@ -2079,8 +2448,8 @@ check_union_p2p_offer (void *cls, unsigned int num_hashes; /* look up elements and send them */ - if ((op->state->phase != PHASE_INVENTORY_PASSIVE) && - (op->state->phase != PHASE_INVENTORY_ACTIVE)) + if ((op->phase != PHASE_INVENTORY_PASSIVE) && + (op->phase != PHASE_INVENTORY_ACTIVE)) { GNUNET_break_op (0); return GNUNET_SYSERR; @@ -2129,7 +2498,7 @@ handle_union_p2p_offer (void *cls, continue; if (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_contains (op->state->demanded_hashes, + GNUNET_CONTAINER_multihashmap_contains (op->demanded_hashes, hash)) { LOG (GNUNET_ERROR_TYPE_DEBUG, @@ -2139,7 +2508,7 @@ handle_union_p2p_offer (void *cls, GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put ( - op->state->demanded_hashes, + op->demanded_hashes, hash, NULL, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)); @@ -2171,12 +2540,11 @@ handle_union_p2p_done (void *cls, { struct Operation *op = cls; - switch (op->state->phase) + switch (op->phase) { case PHASE_INVENTORY_PASSIVE: /* We got all requests, but still have to send our elements in response. */ - op->state->phase = PHASE_FINISH_WAITING; - + op->phase = PHASE_FINISH_WAITING; LOG (GNUNET_ERROR_TYPE_DEBUG, "got DONE (as passive partner), waiting for our demands to be satisfied\n"); /* The active peer is done sending offers @@ -2190,7 +2558,6 @@ handle_union_p2p_done (void *cls, */GNUNET_CADET_receive_done (op->channel); maybe_finish (op); return; - case PHASE_INVENTORY_ACTIVE: LOG (GNUNET_ERROR_TYPE_DEBUG, "got DONE (as active partner), waiting to finish\n"); @@ -2200,11 +2567,10 @@ handle_union_p2p_done (void *cls, * * We'll close the channel * to the other peer once our demands are met. - */op->state->phase = PHASE_FINISH_CLOSING; + */op->phase = PHASE_FINISH_CLOSING; GNUNET_CADET_receive_done (op->channel); maybe_finish (op); return; - default: GNUNET_break_op (0); fail_union_operation (op); @@ -2230,15 +2596,15 @@ handle_union_p2p_over (void *cls, /** * Initiate operation to evaluate a set union with a remote peer. * - * @param op operation to perform (to be initialized) + * @param[in,out] op operation to perform (to be initialized) * @param opaque_context message to be transmitted to the listener * to convince it to accept, may be NULL + * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure */ -static struct OperationState * +static int union_evaluate (struct Operation *op, const struct GNUNET_MessageHeader *opaque_context) { - struct OperationState *state; struct GNUNET_MQ_Envelope *ev; struct OperationRequestMessage *msg; @@ -2249,16 +2615,15 @@ union_evaluate (struct Operation *op, { /* the context message is too large */ GNUNET_break (0); - return NULL; + return GNUNET_SYSERR; } - state = GNUNET_new (struct OperationState); - state->demanded_hashes = GNUNET_CONTAINER_multihashmap_create (32, - GNUNET_NO); + op->demanded_hashes = GNUNET_CONTAINER_multihashmap_create (32, + GNUNET_NO); /* copy the current generation's strata estimator for this operation */ - state->se = strata_estimator_dup (op->set->state->se); + op->se = strata_estimator_dup (op->set->se); /* we started the operation, thus we have to send the operation request */ - state->phase = PHASE_EXPECT_SE; - state->salt_receive = state->salt_send = 42; // FIXME????? + op->phase = PHASE_EXPECT_SE; + op->salt_receive = op->salt_send = 42; // FIXME????? LOG (GNUNET_ERROR_TYPE_DEBUG, "Initiating union operation evaluation\n"); GNUNET_STATISTICS_update (_GSS_statistics, @@ -2278,12 +2643,9 @@ union_evaluate (struct Operation *op, else LOG (GNUNET_ERROR_TYPE_DEBUG, "sent op request without context message\n"); - - op->state = state; initialize_key_to_element (op); - state->initial_size = GNUNET_CONTAINER_multihashmap32_size ( - state->key_to_element); - return state; + op->initial_size = GNUNET_CONTAINER_multihashmap32_size (op->key_to_element); + return GNUNET_OK; } @@ -2312,100 +2674,6 @@ get_incoming (uint32_t id) } -/** - * Destroy an incoming request from a remote peer - * - * @param op remote request to destroy - */ -static void -incoming_destroy (struct Operation *op) -{ - struct Listener *listener; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Destroying incoming operation %p\n", - op); - if (NULL != (listener = op->listener)) - { - GNUNET_CONTAINER_DLL_remove (listener->op_head, - listener->op_tail, - op); - op->listener = NULL; - } - if (NULL != op->timeout_task) - { - GNUNET_SCHEDULER_cancel (op->timeout_task); - op->timeout_task = NULL; - } - _GSS_operation_destroy2 (op); -} - - -/** - * Is element @a ee part of the set used by @a op? - * - * @param ee element to test - * @param op operation the defines the set and its generation - * @return #GNUNET_YES if the element is in the set, #GNUNET_NO if not - */ -int -_GSS_is_element_of_operation (struct ElementEntry *ee, - struct Operation *op) -{ - return ee->generation >= op->generation_created; -} - - -/** - * Destroy the given operation. Used for any operation where both - * peers were known and that thus actually had a vt and channel. Must - * not be used for operations where 'listener' is still set and we do - * not know the other peer. - * - * Call the implementation-specific cancel function of the operation. - * Disconnects from the remote peer. Does not disconnect the client, - * as there may be multiple operations per set. - * - * @param op operation to destroy - */ -void -_GSS_operation_destroy (struct Operation *op) -{ - struct Set *set = op->set; - struct GNUNET_CADET_Channel *channel; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Destroying operation %p\n", op); - GNUNET_assert (NULL == op->listener); - if (NULL != op->state) - { - union_op_cancel (op); - op->state = NULL; - } - if (NULL != set) - { - GNUNET_CONTAINER_DLL_remove (set->ops_head, - set->ops_tail, - op); - op->set = NULL; - } - if (NULL != op->context_msg) - { - GNUNET_free (op->context_msg); - op->context_msg = NULL; - } - if (NULL != (channel = op->channel)) - { - /* This will free op; called conditionally as this helper function - is also called from within the channel disconnect handler. */ - op->channel = NULL; - GNUNET_CADET_channel_destroy (channel); - } - /* We rely on the channel end handler to free 'op'. When 'op->channel' was NULL, - * there was a channel end handler that will free 'op' on the call stack. */ -} - - /** * Callback called when a client connects to the service. * @@ -2479,14 +2747,11 @@ client_disconnect_cb (void *cls, _GSS_operation_destroy (set->ops_head); /* Destroy operation-specific state */ - GNUNET_assert (NULL != set->state); - if (NULL != set->state->se) + if (NULL != set->se) { - strata_estimator_destroy (set->state->se); - set->state->se = NULL; + strata_estimator_destroy (set->se); + set->se = NULL; } - GNUNET_free (set->state); - /* free set content (or at least decrement RC) */ set->content = NULL; GNUNET_assert (0 != content->refcount); @@ -2665,12 +2930,12 @@ handle_client_create_set (void *cls, } set = GNUNET_new (struct Set); { - struct SetState *set_state; + struct StrataEstimator *se; - set_state = GNUNET_new (struct SetState); // FIXME: avoid this malloc, merge structs! - set_state->se = strata_estimator_create (SE_STRATA_COUNT, - SE_IBF_SIZE, SE_IBF_HASH_NUM); - if (NULL == set_state->se) + se = strata_estimator_create (SE_STRATA_COUNT, + SE_IBF_SIZE, + SE_IBF_HASH_NUM); + if (NULL == se) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to allocate strata estimator\n"); @@ -2678,7 +2943,7 @@ handle_client_create_set (void *cls, GNUNET_SERVICE_client_drop (cs->client); return; } - set->state = set_state; + set->se = se; } set->content = GNUNET_new (struct SetContent); set->content->refcount = 1; @@ -2781,37 +3046,6 @@ channel_end_cb (void *channel_ctx, } -/** - * This function probably should not exist - * and be replaced by inlining more specific - * logic in the various places where it is called. - */ -void -_GSS_operation_destroy2 (struct Operation *op) -{ - struct GNUNET_CADET_Channel *channel; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "channel_end_cb called\n"); - if (NULL != (channel = op->channel)) - { - /* This will free op; called conditionally as this helper function - is also called from within the channel disconnect handler. */ - op->channel = NULL; - GNUNET_CADET_channel_destroy (channel); - } - if (NULL != op->listener) - { - incoming_destroy (op); - return; - } - if (NULL != op->set) - send_client_done (op); - _GSS_operation_destroy (op); - GNUNET_free (op); -} - - /** * Function called whenever an MQ-channel's transmission window size changes. * @@ -3042,7 +3276,7 @@ handle_client_set_add (void *cls, /* same element inserted twice */ return; } - strata_estimator_insert (set->state->se, + strata_estimator_insert (set->se, get_ibf_key (&ee->element_hash)); } @@ -3188,8 +3422,10 @@ handle_client_evaluate (void *cls, &channel_end_cb, cadet_handlers); op->mq = GNUNET_CADET_get_mq (op->channel); - op->state = union_evaluate (op, context); - if (NULL == op->state) + // FIXME: inline! + if (GNUNET_OK != + union_evaluate (op, + context)) { GNUNET_break (0); GNUNET_SERVICE_client_drop (cs->client); @@ -3319,7 +3555,7 @@ handle_client_accept (void *cls, interfer with the running operation. */ op->generation_created = set->current_generation; advance_generation (set); - GNUNET_assert (NULL == op->state); + GNUNET_assert (NULL == op->se); LOG (GNUNET_ERROR_TYPE_DEBUG, "accepting set union operation\n"); @@ -3332,7 +3568,6 @@ handle_client_accept (void *cls, 1, GNUNET_NO); { - struct OperationState *state; const struct StrataEstimator *se; struct GNUNET_MQ_Envelope *ev; struct StrataEstimatorMessage *strata_msg; @@ -3340,18 +3575,16 @@ handle_client_accept (void *cls, size_t len; uint16_t type; - state = GNUNET_new (struct OperationState); // FIXME: merge with 'op' to avoid malloc! - state->se = strata_estimator_dup (op->set->state->se); - state->demanded_hashes = GNUNET_CONTAINER_multihashmap_create (32, - GNUNET_NO); - state->salt_receive = state->salt_send = 42; // FIXME????? - op->state = state; + op->se = strata_estimator_dup (op->set->se); + op->demanded_hashes = GNUNET_CONTAINER_multihashmap_create (32, + GNUNET_NO); + op->salt_receive = op->salt_send = 42; // FIXME????? initialize_key_to_element (op); - state->initial_size = GNUNET_CONTAINER_multihashmap32_size ( - state->key_to_element); + op->initial_size = GNUNET_CONTAINER_multihashmap32_size ( + op->key_to_element); /* kick off the operation */ - se = state->se; + se = op->se; buf = GNUNET_malloc (se->strata_count * IBF_BUCKET_SIZE * se->ibf_size); len = strata_estimator_write (se, buf); @@ -3371,9 +3604,7 @@ handle_client_accept (void *cls, op->set->content->elements)); GNUNET_MQ_send (op->mq, ev); - state->phase = PHASE_EXPECT_IBF; - - op->state = state; + op->phase = PHASE_EXPECT_IBF; } /* Now allow CADET to continue, as we did not do this in #handle_incoming_msg (as we wanted to first see if the diff --git a/src/setu/gnunet-service-setu.h b/src/setu/gnunet-service-setu.h deleted file mode 100644 index eb6b7a8e5..000000000 --- a/src/setu/gnunet-service-setu.h +++ /dev/null @@ -1,393 +0,0 @@ -/* - This file is part of GNUnet - Copyright (C) 2013-2017 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - 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 - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @file set/gnunet-service-setu.h - * @brief common components for the implementation the different set operations - * @author Florian Dold - * @author Christian Grothoff - */ -#ifndef GNUNET_SERVICE_SETU_H_PRIVATE -#define GNUNET_SERVICE_SETU_H_PRIVATE - -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_protocols.h" -#include "gnunet_applications.h" -#include "gnunet_core_service.h" -#include "gnunet_cadet_service.h" -#include "gnunet_setu_service.h" -#include "setu.h" - - -/** - * Implementation-specific set state. Used as opaque pointer, and - * specified further in the respective implementation. - */ -struct SetState; - -/** - * Implementation-specific set operation. Used as opaque pointer, and - * specified further in the respective implementation. - */ -struct OperationState; - -/** - * A set that supports a specific operation with other peers. - */ -struct Set; - -/** - * Information about an element element in the set. All elements are - * stored in a hash-table from their hash-code to their 'struct - * Element', so that the remove and add operations are reasonably - * fast. - */ -struct ElementEntry; - -/** - * Operation context used to execute a set operation. - */ -struct Operation; - - -/** - * Information about an element element in the set. All elements are - * stored in a hash-table from their hash-code to their `struct - * Element`, so that the remove and add operations are reasonably - * fast. - */ -struct ElementEntry -{ - /** - * The actual element. The data for the element - * should be allocated at the end of this struct. - */ - struct GNUNET_SETU_Element element; - - /** - * Hash of the element. For set union: Will be used to derive the - * different IBF keys for different salts. - */ - struct GNUNET_HashCode element_hash; - - /** - * First generation that includes this element. - */ - unsigned int generation; - - /** - * #GNUNET_YES if the element is a remote element, and does not belong - * to the operation's set. - */ - int remote; -}; - - -/** - * A listener is inhabited by a client, and waits for evaluation - * requests from remote peers. - */ -struct Listener; - - -/** - * State we keep per client. - */ -struct ClientState -{ - /** - * Set, if associated with the client, otherwise NULL. - */ - struct Set *set; - - /** - * Listener, if associated with the client, otherwise NULL. - */ - struct Listener *listener; - - /** - * Client handle. - */ - struct GNUNET_SERVICE_Client *client; - - /** - * Message queue. - */ - struct GNUNET_MQ_Handle *mq; -}; - - -/** - * Operation context used to execute a set operation. - */ -struct Operation -{ - /** - * Kept in a DLL of the listener, if @e listener is non-NULL. - */ - struct Operation *next; - - /** - * Kept in a DLL of the listener, if @e listener is non-NULL. - */ - struct Operation *prev; - - /** - * Channel to the peer. - */ - struct GNUNET_CADET_Channel *channel; - - /** - * Port this operation runs on. - */ - struct Listener *listener; - - /** - * Message queue for the channel. - */ - struct GNUNET_MQ_Handle *mq; - - /** - * Context message, may be NULL. - */ - struct GNUNET_MessageHeader *context_msg; - - /** - * Set associated with the operation, NULL until the spec has been - * associated with a set. - */ - struct Set *set; - - /** - * Operation-specific operation state. Note that the exact - * type depends on this being a union or intersection operation - * (and thus on @e vt). - */ - struct OperationState *state; - - /** - * The identity of the requesting peer. Needs to - * be stored here as the op spec might not have been created yet. - */ - struct GNUNET_PeerIdentity peer; - - /** - * Timeout task, if the incoming peer has not been accepted - * after the timeout, it will be disconnected. - */ - struct GNUNET_SCHEDULER_Task *timeout_task; - - /** - * Salt to use for the operation. - */ - uint32_t salt; - - /** - * Remote peers element count - */ - uint32_t remote_element_count; - - /** - * ID used to identify an operation between service and client - */ - uint32_t client_request_id; - - /** - * Always use delta operation instead of sending full sets, - * even it it's less efficient. - */ - int force_delta; - - /** - * Always send full sets, even if delta operations would - * be more efficient. - */ - int force_full; - - /** - * #GNUNET_YES to fail operations where Byzantine faults - * are suspected - */ - int byzantine; - - /** - * Lower bound for the set size, used only when - * byzantine mode is enabled. - */ - int byzantine_lower_bound; - - /** - * Unique request id for the request from a remote peer, sent to the - * client, which will accept or reject the request. Set to '0' iff - * the request has not been suggested yet. - */ - uint32_t suggest_id; - - /** - * Generation in which the operation handle - * was created. - */ - unsigned int generation_created; -}; - - -/** - * SetContent stores the actual set elements, which may be shared by - * multiple generations derived from one set. - */ -struct SetContent -{ - /** - * Maps `struct GNUNET_HashCode *` to `struct ElementEntry *`. - */ - struct GNUNET_CONTAINER_MultiHashMap *elements; - - /** - * Number of references to the content. - */ - unsigned int refcount; - - /** - * FIXME: document! - */ - unsigned int latest_generation; - - /** - * Number of concurrently active iterators. - */ - int iterator_count; -}; - - -struct GenerationRange -{ - /** - * First generation that is excluded. - */ - unsigned int start; - - /** - * Generation after the last excluded generation. - */ - unsigned int end; -}; - - -/** - * A set that supports a specific operation with other peers. - */ -struct Set -{ - /** - * Sets are held in a doubly linked list (in `sets_head` and `sets_tail`). - */ - struct Set *next; - - /** - * Sets are held in a doubly linked list. - */ - struct Set *prev; - - /** - * Client that owns the set. Only one client may own a set, - * and there can only be one set per client. - */ - struct ClientState *cs; - - /** - * Content, possibly shared by multiple sets, - * and thus reference counted. - */ - struct SetContent *content; - - /** - * Implementation-specific state. - */ - struct SetState *state; - - /** - * Evaluate operations are held in a linked list. - */ - struct Operation *ops_head; - - /** - * Evaluate operations are held in a linked list. - */ - struct Operation *ops_tail; - - /** - * Current generation, that is, number of previously executed - * operations and lazy copies on the underlying set content. - */ - unsigned int current_generation; - -}; - - -extern struct GNUNET_STATISTICS_Handle *_GSS_statistics; - - -/** - * Destroy the given operation. Used for any operation where both - * peers were known and that thus actually had a vt and channel. Must - * not be used for operations where 'listener' is still set and we do - * not know the other peer. - * - * Call the implementation-specific cancel function of the operation. - * Disconnects from the remote peer. Does not disconnect the client, - * as there may be multiple operations per set. - * - * @param op operation to destroy - */ -void -_GSS_operation_destroy (struct Operation *op); - - -/** - * This function probably should not exist - * and be replaced by inlining more specific - * logic in the various places where it is called. - */ -void -_GSS_operation_destroy2 (struct Operation *op); - - -/** - * Get the table with implementing functions for set union. - * - * @return the operation specific VTable - */ -const struct SetVT * -_GSS_union_vt (void); - - -/** - * Is element @a ee part of the set used by @a op? - * - * @param ee element to test - * @param op operation the defines the set and its generation - * @return #GNUNET_YES if the element is in the set, #GNUNET_NO if not - */ -int -_GSS_is_element_of_operation (struct ElementEntry *ee, - struct Operation *op); - - -#endif -- cgit v1.2.3 From 0c0cbfb5913b87135b51798d8c08cd49951e51f2 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 18 Aug 2020 15:12:56 +0200 Subject: -refactor a bit more --- src/setu/gnunet-service-setu.c | 264 +++++++++++++++++------------------------ 1 file changed, 109 insertions(+), 155 deletions(-) diff --git a/src/setu/gnunet-service-setu.c b/src/setu/gnunet-service-setu.c index c59d375cf..e61e1ab0c 100644 --- a/src/setu/gnunet-service-setu.c +++ b/src/setu/gnunet-service-setu.c @@ -122,16 +122,13 @@ enum UnionOperationPhase PHASE_FINISH_CLOSING, /** - * In the penultimate phase, - * we wait until all our demands - * are satisfied. Then we send a done - * message, and wait for another done message. + * In the penultimate phase, we wait until all our demands are satisfied. + * Then we send a done message, and wait for another done message. */ PHASE_FINISH_WAITING, /** - * In the ultimate phase, we wait until - * our demands are satisfied and then + * In the ultimate phase, we wait until our demands are satisfied and then * quit (sending another DONE message). */ PHASE_DONE, @@ -222,6 +219,18 @@ struct ClientState */ struct Operation { + + /** + * The identity of the requesting peer. Needs to + * be stored here as the op spec might not have been created yet. + */ + struct GNUNET_PeerIdentity peer; + + /** + * Initial size of our set, just before the operation started. + */ + uint64_t initial_size; + /** * Kept in a DLL of the listener, if @e listener is non-NULL. */ @@ -281,6 +290,17 @@ struct Operation */ struct GNUNET_CONTAINER_MultiHashMap32 *key_to_element; + /** + * Timeout task, if the incoming peer has not been accepted + * after the timeout, it will be disconnected. + */ + struct GNUNET_SCHEDULER_Task *timeout_task; + + /** + * Hashes for elements that we have demanded from the other peer. + */ + struct GNUNET_CONTAINER_MultiHashMap *demanded_hashes; + /** * Current state of the operation. */ @@ -296,11 +316,6 @@ struct Operation */ unsigned int ibf_buckets_received; - /** - * Hashes for elements that we have demanded from the other peer. - */ - struct GNUNET_CONTAINER_MultiHashMap *demanded_hashes; - /** * Salt that we're using for sending IBFs */ @@ -322,24 +337,6 @@ struct Operation */ uint32_t received_total; - /** - * Initial size of our set, just before - * the operation started. - */ - uint64_t initial_size; - - /** - * The identity of the requesting peer. Needs to - * be stored here as the op spec might not have been created yet. - */ - struct GNUNET_PeerIdentity peer; - - /** - * Timeout task, if the incoming peer has not been accepted - * after the timeout, it will be disconnected. - */ - struct GNUNET_SCHEDULER_Task *timeout_task; - /** * Salt to use for the operation. */ @@ -450,10 +447,8 @@ struct Set struct SetContent *content; /** - * The strata estimator is only generated once for - * each set. - * The IBF keys are derived from the element hashes with - * salt=0. + * The strata estimator is only generated once for each set. The IBF keys + * are derived from the element hashes with salt=0. */ struct StrataEstimator *se; @@ -495,10 +490,9 @@ struct KeyEntry struct ElementEntry *element; /** - * Did we receive this element? - * Even if element->is_foreign is false, we might - * have received the element, so this indicates that - * the other peer has it. + * Did we receive this element? Even if element->is_foreign is false, we + * might have received the element, so this indicates that the other peer + * has it. */ int received; }; @@ -607,10 +601,9 @@ static unsigned int num_clients; static int in_shutdown; /** - * Counter for allocating unique IDs for clients, used to identify - * incoming operation requests from remote peers, that the client can - * choose to accept or refuse. 0 must not be used (reserved for - * uninitialized). + * Counter for allocating unique IDs for clients, used to identify incoming + * operation requests from remote peers, that the client can choose to accept + * or refuse. 0 must not be used (reserved for uninitialized). */ static uint32_t suggest_id; @@ -643,51 +636,6 @@ destroy_key_to_element_iter (void *cls, } -/** - * Destroy the union operation. Only things specific to the union - * operation are destroyed. - * - * @param op union operation to destroy - */ -static void -union_op_cancel (struct Operation *op) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "destroying union op\n"); - /* check if the op was canceled twice */ - if (NULL != op->remote_ibf) - { - ibf_destroy (op->remote_ibf); - op->remote_ibf = NULL; - } - if (NULL != op->demanded_hashes) - { - GNUNET_CONTAINER_multihashmap_destroy (op->demanded_hashes); - op->demanded_hashes = NULL; - } - if (NULL != op->local_ibf) - { - ibf_destroy (op->local_ibf); - op->local_ibf = NULL; - } - if (NULL != op->se) - { - strata_estimator_destroy (op->se); - op->se = NULL; - } - if (NULL != op->key_to_element) - { - GNUNET_CONTAINER_multihashmap32_iterate (op->key_to_element, - &destroy_key_to_element_iter, - NULL); - GNUNET_CONTAINER_multihashmap32_destroy (op->key_to_element); - op->key_to_element = NULL; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "destroying union op done\n"); -} - - /** * Signal to the client that the operation has finished and * destroy the operation. @@ -761,11 +709,38 @@ _GSS_operation_destroy (struct Operation *op) struct GNUNET_CADET_Channel *channel; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Destroying operation %p\n", + "Destroying union operation %p\n", op); GNUNET_assert (NULL == op->listener); - // FIXME: inline? - union_op_cancel (op); + /* check if the op was canceled twice */ + if (NULL != op->remote_ibf) + { + ibf_destroy (op->remote_ibf); + op->remote_ibf = NULL; + } + if (NULL != op->demanded_hashes) + { + GNUNET_CONTAINER_multihashmap_destroy (op->demanded_hashes); + op->demanded_hashes = NULL; + } + if (NULL != op->local_ibf) + { + ibf_destroy (op->local_ibf); + op->local_ibf = NULL; + } + if (NULL != op->se) + { + strata_estimator_destroy (op->se); + op->se = NULL; + } + if (NULL != op->key_to_element) + { + GNUNET_CONTAINER_multihashmap32_iterate (op->key_to_element, + &destroy_key_to_element_iter, + NULL); + GNUNET_CONTAINER_multihashmap32_destroy (op->key_to_element); + op->key_to_element = NULL; + } if (NULL != set) { GNUNET_CONTAINER_DLL_remove (set->ops_head, @@ -1128,8 +1103,7 @@ init_key_to_element_iterator (void *cls, /** - * Initialize the IBF key to element mapping local to this set - * operation. + * Initialize the IBF key to element mapping local to this set operation. * * @param op the set union operation */ @@ -2593,62 +2567,6 @@ handle_union_p2p_over (void *cls, } -/** - * Initiate operation to evaluate a set union with a remote peer. - * - * @param[in,out] op operation to perform (to be initialized) - * @param opaque_context message to be transmitted to the listener - * to convince it to accept, may be NULL - * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure - */ -static int -union_evaluate (struct Operation *op, - const struct GNUNET_MessageHeader *opaque_context) -{ - struct GNUNET_MQ_Envelope *ev; - struct OperationRequestMessage *msg; - - ev = GNUNET_MQ_msg_nested_mh (msg, - GNUNET_MESSAGE_TYPE_SETU_P2P_OPERATION_REQUEST, - opaque_context); - if (NULL == ev) - { - /* the context message is too large */ - GNUNET_break (0); - return GNUNET_SYSERR; - } - op->demanded_hashes = GNUNET_CONTAINER_multihashmap_create (32, - GNUNET_NO); - /* copy the current generation's strata estimator for this operation */ - op->se = strata_estimator_dup (op->set->se); - /* we started the operation, thus we have to send the operation request */ - op->phase = PHASE_EXPECT_SE; - op->salt_receive = op->salt_send = 42; // FIXME????? - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Initiating union operation evaluation\n"); - GNUNET_STATISTICS_update (_GSS_statistics, - "# of total union operations", - 1, - GNUNET_NO); - GNUNET_STATISTICS_update (_GSS_statistics, - "# of initiated union operations", - 1, - GNUNET_NO); - GNUNET_MQ_send (op->mq, - ev); - - if (NULL != opaque_context) - LOG (GNUNET_ERROR_TYPE_DEBUG, - "sent op request with context message\n"); - else - LOG (GNUNET_ERROR_TYPE_DEBUG, - "sent op request without context message\n"); - initialize_key_to_element (op); - op->initial_size = GNUNET_CONTAINER_multihashmap32_size (op->key_to_element); - return GNUNET_OK; -} - - /** * Get the incoming socket associated with the given id. * @@ -3422,14 +3340,49 @@ handle_client_evaluate (void *cls, &channel_end_cb, cadet_handlers); op->mq = GNUNET_CADET_get_mq (op->channel); - // FIXME: inline! - if (GNUNET_OK != - union_evaluate (op, - context)) { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (cs->client); - return; + struct GNUNET_MQ_Envelope *ev; + struct OperationRequestMessage *msg; + + ev = GNUNET_MQ_msg_nested_mh (msg, + GNUNET_MESSAGE_TYPE_SETU_P2P_OPERATION_REQUEST, + context); + if (NULL == ev) + { + /* the context message is too large */ + GNUNET_break (0); + GNUNET_SERVICE_client_drop (cs->client); + return; + } + op->demanded_hashes = GNUNET_CONTAINER_multihashmap_create (32, + GNUNET_NO); + /* copy the current generation's strata estimator for this operation */ + op->se = strata_estimator_dup (op->set->se); + /* we started the operation, thus we have to send the operation request */ + op->phase = PHASE_EXPECT_SE; + op->salt_receive = op->salt_send = 42; // FIXME????? + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Initiating union operation evaluation\n"); + GNUNET_STATISTICS_update (_GSS_statistics, + "# of total union operations", + 1, + GNUNET_NO); + GNUNET_STATISTICS_update (_GSS_statistics, + "# of initiated union operations", + 1, + GNUNET_NO); + GNUNET_MQ_send (op->mq, + ev); + if (NULL != context) + LOG (GNUNET_ERROR_TYPE_DEBUG, + "sent op request with context message\n"); + else + LOG (GNUNET_ERROR_TYPE_DEBUG, + "sent op request without context message\n"); + initialize_key_to_element (op); + op->initial_size = GNUNET_CONTAINER_multihashmap32_size ( + op->key_to_element); + } GNUNET_SERVICE_client_continue (cs->client); } @@ -3657,7 +3610,8 @@ run (void *cls, forcefully disconnected! */ GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); - _GSS_statistics = GNUNET_STATISTICS_create ("setu", cfg); + _GSS_statistics = GNUNET_STATISTICS_create ("setu", + cfg); cadet = GNUNET_CADET_connect (cfg); if (NULL == cadet) { -- cgit v1.2.3 From 4d607f2f2838431cc7a349441f8f018ab99633a2 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 18 Aug 2020 18:09:58 +0200 Subject: splitting of set intersection functionality from set service (not yet finished, FTBFS) --- configure.ac | 2 + po/POTFILES.in | 5 + src/include/gnunet_protocols.h | 94 +- src/include/gnunet_seti_service.h | 369 ++++ src/include/gnunet_setu_service.h | 4 +- src/seti/.gitignore | 3 + src/seti/Makefile.am | 90 + src/seti/gnunet-service-seti.c | 3274 +++++++++++++++++++++++++++++++ src/seti/gnunet-service-seti_protocol.h | 144 ++ src/seti/gnunet-seti-profiler.c | 480 +++++ src/seti/plugin_block_seti_test.c | 123 ++ src/seti/seti.conf.in | 12 + src/seti/seti.h | 267 +++ src/seti/seti_api.c | 895 +++++++++ src/seti/test_seti.conf | 33 + src/seti/test_seti_api.c | 393 ++++ 16 files changed, 6172 insertions(+), 16 deletions(-) create mode 100644 src/include/gnunet_seti_service.h create mode 100644 src/seti/.gitignore create mode 100644 src/seti/Makefile.am create mode 100644 src/seti/gnunet-service-seti.c create mode 100644 src/seti/gnunet-service-seti_protocol.h create mode 100644 src/seti/gnunet-seti-profiler.c create mode 100644 src/seti/plugin_block_seti_test.c create mode 100644 src/seti/seti.conf.in create mode 100644 src/seti/seti.h create mode 100644 src/seti/seti_api.c create mode 100644 src/seti/test_seti.conf create mode 100644 src/seti/test_seti_api.c diff --git a/configure.ac b/configure.ac index 72309c78d..bd92bd0e9 100644 --- a/configure.ac +++ b/configure.ac @@ -1939,6 +1939,8 @@ src/scalarproduct/Makefile src/scalarproduct/scalarproduct.conf src/set/Makefile src/set/set.conf +src/seti/Makefile +src/seti/seti.conf src/setu/Makefile src/setu/setu.conf src/sq/Makefile diff --git a/po/POTFILES.in b/po/POTFILES.in index 7d19122ca..12e27fa81 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -333,6 +333,11 @@ src/set/ibf.c src/set/ibf_sim.c src/set/plugin_block_set_test.c src/set/set_api.c +src/seti/gnunet-service-set_intersection.c +src/seti/gnunet-service-seti.c +src/seti/gnunet-seti-profiler.c +src/seti/plugin_block_seti_test.c +src/seti/setu_api.c src/setu/gnunet-service-setu.c src/setu/gnunet-service-setu_strata_estimator.c src/setu/gnunet-setu-ibf-profiler.c diff --git a/src/include/gnunet_protocols.h b/src/include/gnunet_protocols.h index c3fcde0b9..e9a2b1c0e 100644 --- a/src/include/gnunet_protocols.h +++ b/src/include/gnunet_protocols.h @@ -1713,80 +1713,146 @@ extern "C" { * Demand the whole element from the other * peer, given only the hash code. */ -#define GNUNET_MESSAGE_TYPE_SETU_P2P_REQUEST_FULL 565 +#define GNUNET_MESSAGE_TYPE_SETU_P2P_REQUEST_FULL 559 /** * Demand the whole element from the other * peer, given only the hash code. */ -#define GNUNET_MESSAGE_TYPE_SETU_P2P_DEMAND 566 +#define GNUNET_MESSAGE_TYPE_SETU_P2P_DEMAND 560 /** * Tell the other peer to send us a list of * hashes that match an IBF key. */ -#define GNUNET_MESSAGE_TYPE_SETU_P2P_INQUIRY 567 +#define GNUNET_MESSAGE_TYPE_SETU_P2P_INQUIRY 561 /** * Tell the other peer which hashes match a * given IBF key. */ -#define GNUNET_MESSAGE_TYPE_SETU_P2P_OFFER 568 +#define GNUNET_MESSAGE_TYPE_SETU_P2P_OFFER 562 /** * Request a set union operation from a remote peer. */ -#define GNUNET_MESSAGE_TYPE_SETU_P2P_OPERATION_REQUEST 581 +#define GNUNET_MESSAGE_TYPE_SETU_P2P_OPERATION_REQUEST 563 /** * Strata estimator. */ -#define GNUNET_MESSAGE_TYPE_SETU_P2P_SE 582 +#define GNUNET_MESSAGE_TYPE_SETU_P2P_SE 564 /** * Invertible bloom filter. */ -#define GNUNET_MESSAGE_TYPE_SETU_P2P_IBF 583 +#define GNUNET_MESSAGE_TYPE_SETU_P2P_IBF 565 /** * Actual set elements. */ -#define GNUNET_MESSAGE_TYPE_SETU_P2P_ELEMENTS 584 +#define GNUNET_MESSAGE_TYPE_SETU_P2P_ELEMENTS 566 /** * Requests for the elements with the given hashes. */ -#define GNUNET_MESSAGE_TYPE_SETU_P2P_ELEMENT_REQUESTS 585 +#define GNUNET_MESSAGE_TYPE_SETU_P2P_ELEMENT_REQUESTS 567 /** * Set operation is done. */ -#define GNUNET_MESSAGE_TYPE_SETU_P2P_DONE 586 +#define GNUNET_MESSAGE_TYPE_SETU_P2P_DONE 568 /** * Compressed strata estimator. */ -#define GNUNET_MESSAGE_TYPE_SETU_P2P_SEC 590 +#define GNUNET_MESSAGE_TYPE_SETU_P2P_SEC 569 /** * Request all missing elements from the other peer, * based on their sets and the elements we previously sent * with #GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENTS. */ -#define GNUNET_MESSAGE_TYPE_SETU_P2P_FULL_DONE 597 +#define GNUNET_MESSAGE_TYPE_SETU_P2P_FULL_DONE 570 /** * Send a set element, not as response to a demand but because * we're sending the full set. */ -#define GNUNET_MESSAGE_TYPE_SETU_P2P_FULL_ELEMENT 598 +#define GNUNET_MESSAGE_TYPE_SETU_P2P_FULL_ELEMENT 571 /** * Request all missing elements from the other peer, * based on their sets and the elements we previously sent * with #GNUNET_MESSAGE_TYPE_SET_P2P_ELEMENTS. */ -#define GNUNET_MESSAGE_TYPE_SETU_P2P_OVER 599 +#define GNUNET_MESSAGE_TYPE_SETU_P2P_OVER 572 + + +/******************************************************************************* + * SETI message types + ******************************************************************************/ + + +/** + * Cancel a set operation + */ +#define GNUNET_MESSAGE_TYPE_SETI_CANCEL 580 + +/** + * Add element to set. + */ +#define GNUNET_MESSAGE_TYPE_SETI_ADD 581 + +/** + * Create a new local set + */ +#define GNUNET_MESSAGE_TYPE_SETI_CREATE 582 + +/** + * Handle result message from operation + */ +#define GNUNET_MESSAGE_TYPE_SETI_RESULT 583 + +/** + * Evaluate a set operation + */ +#define GNUNET_MESSAGE_TYPE_SETI_EVALUATE 584 + +/** + * Listen for operation requests + */ +#define GNUNET_MESSAGE_TYPE_SETI_LISTEN 585 + +/** + * Reject a set request. + */ +#define GNUNET_MESSAGE_TYPE_SETI_REJECT 586 + +/** + * Accept an incoming set request + */ +#define GNUNET_MESSAGE_TYPE_SETI_ACCEPT 587 + +/** + * Notify the client of an incoming request from a remote peer + */ +#define GNUNET_MESSAGE_TYPE_SETI_REQUEST 588 + +/** + * Information about the element count for intersection + */ +#define GNUNET_MESSAGE_TYPE_SETI_P2P_ELEMENT_INFO 591 + +/** + * Bloom filter message for intersection exchange started by Bob. + */ +#define GNUNET_MESSAGE_TYPE_SETI_P2P_BF 592 + +/** + * Intersection operation is done. + */ +#define GNUNET_MESSAGE_TYPE_SETI_P2P_DONE 593 /******************************************************************************* diff --git a/src/include/gnunet_seti_service.h b/src/include/gnunet_seti_service.h new file mode 100644 index 000000000..c0b6f41a5 --- /dev/null +++ b/src/include/gnunet_seti_service.h @@ -0,0 +1,369 @@ +/* + This file is part of GNUnet + Copyright (C) 2013, 2014, 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Florian Dold + * @author Christian Grothoff + * + * @file + * Two-peer set intersection operations + * + * @defgroup set Set intersection service + * Two-peer set operations + * + * @{ + */ + +#ifndef GNUNET_SETI_SERVICE_H +#define GNUNET_SETI_SERVICE_H + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include "gnunet_common.h" +#include "gnunet_time_lib.h" +#include "gnunet_configuration_lib.h" + + +/** + * Maximum size of a context message for set operation requests. + */ +#define GNUNET_SETI_CONTEXT_MESSAGE_MAX_SIZE ((1 << 16) - 1024) + +/** + * Opaque handle to a set. + */ +struct GNUNET_SETI_Handle; + +/** + * Opaque handle to a set operation request from another peer. + */ +struct GNUNET_SETI_Request; + +/** + * Opaque handle to a listen operation. + */ +struct GNUNET_SETI_ListenHandle; + +/** + * Opaque handle to a set operation. + */ +struct GNUNET_SETI_OperationHandle; + + +/** + * Status for the result callback + */ +enum GNUNET_SETI_Status +{ + + /** + * Element should be added to the result set of the local peer, i.e. the + * element is in the intersection. + */ + GNUNET_SETI_STATUS_ADD_LOCAL, + + /** + * Element should be delete from the result set of the local peer, i.e. the + * local peer is having an element that is not in the intersection. + */ + GNUNET_SETI_STATUS_DEL_LOCAL, + + /** + * The other peer refused to do the operation with us, or something went + * wrong. + */ + GNUNET_SETI_STATUS_FAILURE, + + /** + * Success, all elements have been sent (and received). + */ + GNUNET_SETI_STATUS_DONE +}; + + +/** + * Element stored in a set. + */ +struct GNUNET_SETI_Element +{ + /** + * Number of bytes in the buffer pointed to by data. + */ + uint16_t size; + + /** + * Application-specific element type. + */ + uint16_t element_type; + + /** + * Actual data of the element + */ + const void *data; +}; + + +/** + * Possible options to pass to a set operation. + * + * Used as tag for struct #GNUNET_SETI_Option. + */ +enum GNUNET_SETI_OptionType +{ + /** + * List terminator. + */ + GNUNET_SETI_OPTION_END = 0, + + /** + * Return the elements remaining in the intersection + * (#GNUNET_SETI_STATUS_ADD_LOCAL). If not given, the default is to return a + * list of the elements to be removed (#GNUNET_SETI_STATUS_DEL_LOCAL). + */ + GNUNET_SETI_OPTION_RETURN_INTERSECTION = 1, +}; + + +/** + * Option for set operations. + */ +struct GNUNET_SETI_Option +{ + /** + * Type of the option. + */ + enum GNUNET_SETI_OptionType type; + + /** + * Value for the option, only used with some options. + */ + union + { + uint64_t num; + } v; +}; + + +/** + * Callback for set union operation results. Called for each element + * in the result set. + * + * @param cls closure + * @param element a result element, only valid if status is #GNUNET_SETI_STATUS_OK + * @param current_size current set size + * @param status see `enum GNUNET_SETI_Status` + */ +typedef void +(*GNUNET_SETI_ResultIterator) (void *cls, + const struct GNUNET_SETI_Element *element, + uint64_t current_size, + enum GNUNET_SETI_Status status); + + +/** + * Called when another peer wants to do a set operation with the + * local peer. If a listen error occurs, the @a request is NULL. + * + * @param cls closure + * @param other_peer the other peer + * @param context_msg message with application specific information from + * the other peer + * @param request request from the other peer (never NULL), use GNUNET_SETI_accept() + * to accept it, otherwise the request will be refused + * Note that we can't just return value from the listen callback, + * as it is also necessary to specify the set we want to do the + * operation with, whith sometimes can be derived from the context + * message. It's necessary to specify the timeout. + */ +typedef void +(*GNUNET_SETI_ListenCallback) (void *cls, + const struct GNUNET_PeerIdentity *other_peer, + const struct GNUNET_MessageHeader *context_msg, + struct GNUNET_SETI_Request *request); + + +/** + * Create an empty set, supporting the specified operation. + * + * @param cfg configuration to use for connecting to the + * set service + * @return a handle to the set + */ +struct GNUNET_SETI_Handle * +GNUNET_SETI_create (const struct GNUNET_CONFIGURATION_Handle *cfg); + + +/** + * Add an element to the given set. + * + * @param set set to add element to + * @param element element to add to the set + * @param cb function to call when finished, can be NULL + * @param cb_cls closure for @a cb + * @return #GNUNET_OK on success, #GNUNET_SYSERR if the + * set is invalid (e.g. the set service crashed) + */ +int +GNUNET_SETI_add_element (struct GNUNET_SETI_Handle *set, + const struct GNUNET_SETI_Element *element, + GNUNET_SCHEDULER_TaskCallback cb, + void *cb_cls); + + +/** + * Destroy the set handle, and free all associated resources. Operations may + * still be pending when a set is destroyed (and will be allowed to complete). + * + * @param set set to destroy + */ +void +GNUNET_SETI_destroy (struct GNUNET_SETI_Handle *set); + + +/** + * Prepare a set operation to be evaluated with another peer. The evaluation + * will not start until the client provides a local set with + * GNUNET_SETI_commit(). + * + * @param other_peer peer with the other set + * @param app_id hash for the application using the set + * @param context_msg additional information for the request + * @param options options to use when processing the request + * @param result_cb called on error or success + * @param result_cls closure for @a result_cb + * @return a handle to cancel the operation + */ +struct GNUNET_SETI_OperationHandle * +GNUNET_SETI_prepare (const struct GNUNET_PeerIdentity *other_peer, + const struct GNUNET_HashCode *app_id, + const struct GNUNET_MessageHeader *context_msg, + const struct GNUNET_SETI_Option options[], + GNUNET_SETI_ResultIterator result_cb, + void *result_cls); + + +/** + * Wait for set operation requests for the given application ID. + * If the connection to the set service is lost, the listener is + * re-created transparently with exponential backoff. + * + * @param cfg configuration to use for connecting to + * the set service + * @param app_id id of the application that handles set operation requests + * @param listen_cb called for each incoming request matching the operation + * and application id + * @param listen_cls handle for @a listen_cb + * @return a handle that can be used to cancel the listen operation + */ +struct GNUNET_SETI_ListenHandle * +GNUNET_SETI_listen (const struct GNUNET_CONFIGURATION_Handle *cfg, + const struct GNUNET_HashCode *app_id, + GNUNET_SETI_ListenCallback listen_cb, + void *listen_cls); + + +/** + * Cancel the given listen operation. After calling cancel, the + * listen callback for this listen handle will not be called again. + * Note that cancelling a listen operation will automatically reject + * all operations that have not yet been accepted. + * + * @param lh handle for the listen operation + */ +void +GNUNET_SETI_listen_cancel (struct GNUNET_SETI_ListenHandle *lh); + + +/** + * Accept a request we got via GNUNET_SETI_listen(). Must be called during + * GNUNET_SETI_listen(), as the `struct GNUNET_SETI_Request` becomes invalid + * afterwards. + * Call GNUNET_SETI_commit() to provide the local set to use for the operation, + * and to begin the exchange with the remote peer. + * + * @param request request to accept + * @param options options to use when processing the request + * @param result_cb callback for the results + * @param result_cls closure for @a result_cb + * @return a handle to cancel the operation + */ +struct GNUNET_SETI_OperationHandle * +GNUNET_SETI_accept (struct GNUNET_SETI_Request *request, + const struct GNUNET_SETI_Option options[], + GNUNET_SETI_ResultIterator result_cb, + void *result_cls); + + +/** + * Commit a set to be used with a set operation. + * This function is called once we have fully constructed + * the set that we want to use for the operation. At this + * time, the P2P protocol can then begin to exchange the + * set information and call the result callback with the + * result information. + * + * @param oh handle to the set operation + * @param set the set to use for the operation + * @return #GNUNET_OK on success, #GNUNET_SYSERR if the + * set is invalid (e.g. the set service crashed) + */ +int +GNUNET_SETI_commit (struct GNUNET_SETI_OperationHandle *oh, + struct GNUNET_SETI_Handle *set); + + +/** + * Cancel the given set operation. May not be called after the operation's + * `GNUNET_SETI_ResultIterator` has been called with a status of + * #GNUNET_SETI_STATUS_FAILURE or #GNUNET_SETI_STATUS_DONE. + * + * @param oh set operation to cancel + */ +void +GNUNET_SETI_operation_cancel (struct GNUNET_SETI_OperationHandle *oh); + + +/** + * Hash a set element. + * + * @param element the element that should be hashed + * @param[out] ret_hash a pointer to where the hash of @a element + * should be stored + */ +void +GNUNET_SETI_element_hash (const struct GNUNET_SETI_Element *element, + struct GNUNET_HashCode *ret_hash); + + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + +#endif + +/** @} */ /* end of group */ diff --git a/src/include/gnunet_setu_service.h b/src/include/gnunet_setu_service.h index 092c03198..d737b97c1 100644 --- a/src/include/gnunet_setu_service.h +++ b/src/include/gnunet_setu_service.h @@ -22,9 +22,9 @@ * @author Christian Grothoff * * @file - * Two-peer set operations + * Two-peer set union operations * - * @defgroup set Set service + * @defgroup set Set union service * Two-peer set operations * * @{ diff --git a/src/seti/.gitignore b/src/seti/.gitignore new file mode 100644 index 000000000..5f234a4c2 --- /dev/null +++ b/src/seti/.gitignore @@ -0,0 +1,3 @@ +gnunet-seti-profiler +gnunet-service-seti +test_seti_api diff --git a/src/seti/Makefile.am b/src/seti/Makefile.am new file mode 100644 index 000000000..d96ffff03 --- /dev/null +++ b/src/seti/Makefile.am @@ -0,0 +1,90 @@ +# This Makefile.am is in the public domain +AM_CPPFLAGS = -I$(top_srcdir)/src/include + +pkgcfgdir= $(pkgdatadir)/config.d/ + +libexecdir= $(pkglibdir)/libexec/ + +plugindir = $(libdir)/gnunet + +pkgcfg_DATA = \ + seti.conf + +if USE_COVERAGE + AM_CFLAGS = -fprofile-arcs -ftest-coverage +endif + +if HAVE_TESTING +bin_PROGRAMS = \ + gnunet-seti-profiler +endif + +libexec_PROGRAMS = \ + gnunet-service-seti + +lib_LTLIBRARIES = \ + libgnunetseti.la + +gnunet_seti_profiler_SOURCES = \ + gnunet-seti-profiler.c +gnunet_seti_profiler_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + libgnunetseti.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(GN_LIBINTL) + + +gnunet_service_seti_SOURCES = \ + gnunet-service-seti.c \ + gnunet-service-set_protocol.h +gnunet_service_seti_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/cadet/libgnunetcadet.la \ + $(top_builddir)/src/block/libgnunetblock.la \ + libgnunetseti.la \ + $(GN_LIBINTL) + +libgnunetseti_la_SOURCES = \ + seti_api.c seti.h +libgnunetseti_la_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(LTLIBINTL) +libgnunetseti_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) + +if HAVE_TESTING +check_PROGRAMS = \ + test_seti_api +endif + +if ENABLE_TEST_RUN +AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME; +TESTS = $(check_PROGRAMS) +endif + +test_seti_api_SOURCES = \ + test_seti_api.c +test_seti_api_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ + libgnunetset.la + +plugin_LTLIBRARIES = \ + libgnunet_plugin_block_seti_test.la + +libgnunet_plugin_block_seti_test_la_SOURCES = \ + plugin_block_seti_test.c +libgnunet_plugin_block_seti_test_la_LIBADD = \ + $(top_builddir)/src/block/libgnunetblock.la \ + $(top_builddir)/src/block/libgnunetblockgroup.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(LTLIBINTL) +libgnunet_plugin_block_seti_test_la_LDFLAGS = \ + $(GN_PLUGIN_LDFLAGS) + + +EXTRA_DIST = \ + test_seti.conf diff --git a/src/seti/gnunet-service-seti.c b/src/seti/gnunet-service-seti.c new file mode 100644 index 000000000..3b8da01cd --- /dev/null +++ b/src/seti/gnunet-service-seti.c @@ -0,0 +1,3274 @@ +/* + This file is part of GNUnet + Copyright (C) 2013-2017, 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file set/gnunet-service-seti.c + * @brief two-peer set intersection operations + * @author Florian Dold + * @author Christian Grothoff + */ +#include "gnunet-service-seti_protocol.h" +#include "gnunet_statistics_service.h" + +/** + * How long do we hold on to an incoming channel if there is + * no local listener before giving up? + */ +#define INCOMING_CHANNEL_TIMEOUT GNUNET_TIME_UNIT_MINUTES + + +/** + * Current phase we are in for a intersection operation. + */ +enum IntersectionOperationPhase +{ + /** + * We are just starting. + */ + PHASE_INITIAL, + + /** + * We have send the number of our elements to the other + * peer, but did not setup our element set yet. + */ + PHASE_COUNT_SENT, + + /** + * We have initialized our set and are now reducing it by exchanging + * Bloom filters until one party notices the their element hashes + * are equal. + */ + PHASE_BF_EXCHANGE, + + /** + * We must next send the P2P DONE message (after finishing mostly + * with the local client). Then we will wait for the channel to close. + */ + PHASE_MUST_SEND_DONE, + + /** + * We have received the P2P DONE message, and must finish with the + * local client before terminating the channel. + */ + PHASE_DONE_RECEIVED, + + /** + * The protocol is over. Results may still have to be sent to the + * client. + */ + PHASE_FINISHED +}; + + +/** + * Implementation-specific set state. Used as opaque pointer, and + * specified further in the respective implementation. + */ +struct SetState; + +/** + * Implementation-specific set operation. Used as opaque pointer, and + * specified further in the respective implementation. + */ +struct OperationState; + +/** + * A set that supports a specific operation with other peers. + */ +struct Set; + +/** + * Information about an element element in the set. All elements are + * stored in a hash-table from their hash-code to their 'struct + * Element', so that the remove and add operations are reasonably + * fast. + */ +struct ElementEntry; + +/** + * Operation context used to execute a set operation. + */ +struct Operation; + + +/** + * MutationEvent gives information about changes + * to an element (removal / addition) in a set content. + */ +struct MutationEvent +{ + /** + * First generation affected by this mutation event. + * + * If @a generation is 0, this mutation event is a list + * sentinel element. + */ + unsigned int generation; + + /** + * If @a added is #GNUNET_YES, then this is a + * `remove` event, otherwise it is an `add` event. + */ + int added; +}; + + +/** + * Information about an element element in the set. All elements are + * stored in a hash-table from their hash-code to their `struct + * Element`, so that the remove and add operations are reasonably + * fast. + */ +struct ElementEntry +{ + /** + * The actual element. The data for the element + * should be allocated at the end of this struct. + */ + struct GNUNET_SET_Element element; + + /** + * Hash of the element. For set union: Will be used to derive the + * different IBF keys for different salts. + */ + struct GNUNET_HashCode element_hash; + + /** + * If @a mutations is not NULL, it contains + * a list of mutations, ordered by increasing generation. + * The list is terminated by a sentinel event with `generation` + * set to 0. + * + * If @a mutations is NULL, then this element exists in all generations + * of the respective set content this element belongs to. + */ + struct MutationEvent *mutations; + + /** + * Number of elements in the array @a mutations. + */ + unsigned int mutations_size; + + /** + * #GNUNET_YES if the element is a remote element, and does not belong + * to the operation's set. + */ + int remote; +}; + + +/** + * A listener is inhabited by a client, and waits for evaluation + * requests from remote peers. + */ +struct Listener; + + +/** + * State we keep per client. + */ +struct ClientState +{ + /** + * Set, if associated with the client, otherwise NULL. + */ + struct Set *set; + + /** + * Listener, if associated with the client, otherwise NULL. + */ + struct Listener *listener; + + /** + * Client handle. + */ + struct GNUNET_SERVICE_Client *client; + + /** + * Message queue. + */ + struct GNUNET_MQ_Handle *mq; +}; + + +/** + * Operation context used to execute a set operation. + */ +struct Operation +{ + /** + * Kept in a DLL of the listener, if @e listener is non-NULL. + */ + struct Operation *next; + + /** + * Kept in a DLL of the listener, if @e listener is non-NULL. + */ + struct Operation *prev; + + /** + * Channel to the peer. + */ + struct GNUNET_CADET_Channel *channel; + + /** + * Port this operation runs on. + */ + struct Listener *listener; + + /** + * Message queue for the channel. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Context message, may be NULL. + */ + struct GNUNET_MessageHeader *context_msg; + + /** + * Set associated with the operation, NULL until the spec has been + * associated with a set. + */ + struct Set *set; + + /** + * Operation-specific operation state. Note that the exact + * type depends on this being a union or intersection operation + * (and thus on @e vt). + */ + struct OperationState *state; // FIXME: inline + + /** + * The identity of the requesting peer. Needs to + * be stored here as the op spec might not have been created yet. + */ + struct GNUNET_PeerIdentity peer; + + /** + * Timeout task, if the incoming peer has not been accepted + * after the timeout, it will be disconnected. + */ + struct GNUNET_SCHEDULER_Task *timeout_task; + + /** + * Salt to use for the operation. + */ + uint32_t salt; + + /** + * Remote peers element count + */ + uint32_t remote_element_count; + + /** + * ID used to identify an operation between service and client + */ + uint32_t client_request_id; + + /** + * When are elements sent to the client, and which elements are sent? + */ + enum GNUNET_SET_ResultMode result_mode; + + /** + * Always use delta operation instead of sending full sets, + * even it it's less efficient. + */ + int force_delta; + + /** + * Always send full sets, even if delta operations would + * be more efficient. + */ + int force_full; + + /** + * #GNUNET_YES to fail operations where Byzantine faults + * are suspected + */ + int byzantine; + + /** + * Lower bound for the set size, used only when + * byzantine mode is enabled. + */ + int byzantine_lower_bound; + + /** + * Unique request id for the request from a remote peer, sent to the + * client, which will accept or reject the request. Set to '0' iff + * the request has not been suggested yet. + */ + uint32_t suggest_id; + + /** + * Generation in which the operation handle + * was created. + */ + unsigned int generation_created; +}; + + +/** + * SetContent stores the actual set elements, which may be shared by + * multiple generations derived from one set. + */ +struct SetContent +{ + /** + * Maps `struct GNUNET_HashCode *` to `struct ElementEntry *`. + */ + struct GNUNET_CONTAINER_MultiHashMap *elements; + + /** + * Mutations requested by the client that we're + * unable to execute right now because we're iterating + * over the underlying hash map of elements. + */ + struct PendingMutation *pending_mutations_head; + + /** + * Mutations requested by the client that we're + * unable to execute right now because we're iterating + * over the underlying hash map of elements. + */ + struct PendingMutation *pending_mutations_tail; + + /** + * Number of references to the content. + */ + unsigned int refcount; + + /** + * FIXME: document! + */ + unsigned int latest_generation; + + /** + * Number of concurrently active iterators. + */ + int iterator_count; +}; + + +struct GenerationRange +{ + /** + * First generation that is excluded. + */ + unsigned int start; + + /** + * Generation after the last excluded generation. + */ + unsigned int end; +}; + + +/** + * Information about a mutation to apply to a set. + */ +struct PendingMutation +{ + /** + * Mutations are kept in a DLL. + */ + struct PendingMutation *prev; + + /** + * Mutations are kept in a DLL. + */ + struct PendingMutation *next; + + /** + * Set this mutation is about. + */ + struct Set *set; + + /** + * Message that describes the desired mutation. + * May only be a #GNUNET_MESSAGE_TYPE_SET_ADD or + * #GNUNET_MESSAGE_TYPE_SET_REMOVE. + */ + struct GNUNET_SET_ElementMessage *msg; +}; + + +/** + * A set that supports a specific operation with other peers. + */ +struct Set +{ + /** + * Sets are held in a doubly linked list (in `sets_head` and `sets_tail`). + */ + struct Set *next; + + /** + * Sets are held in a doubly linked list. + */ + struct Set *prev; + + /** + * Client that owns the set. Only one client may own a set, + * and there can only be one set per client. + */ + struct ClientState *cs; + + /** + * Content, possibly shared by multiple sets, + * and thus reference counted. + */ + struct SetContent *content; + + /** + * Implementation-specific state. + */ + struct SetState *state; + + /** + * Current state of iterating elements for the client. + * NULL if we are not currently iterating. + */ + struct GNUNET_CONTAINER_MultiHashMapIterator *iter; + + /** + * Evaluate operations are held in a linked list. + */ + struct Operation *ops_head; + + /** + * Evaluate operations are held in a linked list. + */ + struct Operation *ops_tail; + + /** + * List of generations we have to exclude, due to lazy copies. + */ + struct GenerationRange *excluded_generations; + + /** + * Current generation, that is, number of previously executed + * operations and lazy copies on the underlying set content. + */ + unsigned int current_generation; + + /** + * Number of elements in array @a excluded_generations. + */ + unsigned int excluded_generations_size; + + /** + * Type of operation supported for this set + */ + enum GNUNET_SET_OperationType operation; + + /** + * Generation we're currently iteration over. + */ + unsigned int iter_generation; + + /** + * Each @e iter is assigned a unique number, so that the client + * can distinguish iterations. + */ + uint16_t iteration_id; +}; + + +/** + * State of an evaluate operation with another peer. + */ +struct OperationState +{ + /** + * The bf we currently receive + */ + struct GNUNET_CONTAINER_BloomFilter *remote_bf; + + /** + * BF of the set's element. + */ + struct GNUNET_CONTAINER_BloomFilter *local_bf; + + /** + * Remaining elements in the intersection operation. + * Maps element-id-hashes to 'elements in our set'. + */ + struct GNUNET_CONTAINER_MultiHashMap *my_elements; + + /** + * Iterator for sending the final set of @e my_elements to the client. + */ + struct GNUNET_CONTAINER_MultiHashMapIterator *full_result_iter; + + /** + * Evaluate operations are held in a linked list. + */ + struct OperationState *next; + + /** + * Evaluate operations are held in a linked list. + */ + struct OperationState *prev; + + /** + * For multipart BF transmissions, we have to store the + * bloomfilter-data until we fully received it. + */ + char *bf_data; + + /** + * XOR of the keys of all of the elements (remaining) in my set. + * Always updated when elements are added or removed to + * @e my_elements. + */ + struct GNUNET_HashCode my_xor; + + /** + * XOR of the keys of all of the elements (remaining) in + * the other peer's set. Updated when we receive the + * other peer's Bloom filter. + */ + struct GNUNET_HashCode other_xor; + + /** + * How many bytes of @e bf_data are valid? + */ + uint32_t bf_data_offset; + + /** + * Current element count contained within @e my_elements. + * (May differ briefly during initialization.) + */ + uint32_t my_element_count; + + /** + * size of the bloomfilter in @e bf_data. + */ + uint32_t bf_data_size; + + /** + * size of the bloomfilter + */ + uint32_t bf_bits_per_element; + + /** + * Salt currently used for BF construction (by us or the other peer, + * depending on where we are in the code). + */ + uint32_t salt; + + /** + * Current state of the operation. + */ + enum IntersectionOperationPhase phase; + + /** + * Generation in which the operation handle + * was created. + */ + unsigned int generation_created; + + /** + * Did we send the client that we are done? + */ + int client_done_sent; + + /** + * Set whenever we reach the state where the death of the + * channel is perfectly find and should NOT result in the + * operation being cancelled. + */ + int channel_death_expected; +}; + + +/** + * Extra state required for efficient set intersection. + * Merely tracks the total number of elements. + */ +struct SetState +{ + /** + * Number of currently valid elements in the set which have not been + * removed. + */ + uint32_t current_set_element_count; +}; + + +/** + * A listener is inhabited by a client, and waits for evaluation + * requests from remote peers. + */ +struct Listener +{ + /** + * Listeners are held in a doubly linked list. + */ + struct Listener *next; + + /** + * Listeners are held in a doubly linked list. + */ + struct Listener *prev; + + /** + * Head of DLL of operations this listener is responsible for. + * Once the client has accepted/declined the operation, the + * operation is moved to the respective set's operation DLLS. + */ + struct Operation *op_head; + + /** + * Tail of DLL of operations this listener is responsible for. + * Once the client has accepted/declined the operation, the + * operation is moved to the respective set's operation DLLS. + */ + struct Operation *op_tail; + + /** + * Client that owns the listener. + * Only one client may own a listener. + */ + struct ClientState *cs; + + /** + * The port we are listening on with CADET. + */ + struct GNUNET_CADET_Port *open_port; + + /** + * Application ID for the operation, used to distinguish + * multiple operations of the same type with the same peer. + */ + struct GNUNET_HashCode app_id; + + /** + * The type of the operation. + */ + enum GNUNET_SET_OperationType operation; +}; + + +/** + * Handle to the cadet service, used to listen for and connect to + * remote peers. + */ +static struct GNUNET_CADET_Handle *cadet; + +/** + * Statistics handle. + */ +static struct GNUNET_STATISTICS_Handle *_GSS_statistics; + +/** + * Listeners are held in a doubly linked list. + */ +static struct Listener *listener_head; + +/** + * Listeners are held in a doubly linked list. + */ +static struct Listener *listener_tail; + +/** + * Number of active clients. + */ +static unsigned int num_clients; + +/** + * Are we in shutdown? if #GNUNET_YES and the number of clients + * drops to zero, disconnect from CADET. + */ +static int in_shutdown; + +/** + * Counter for allocating unique IDs for clients, used to identify + * incoming operation requests from remote peers, that the client can + * choose to accept or refuse. 0 must not be used (reserved for + * uninitialized). + */ +static uint32_t suggest_id; + + +/** + * If applicable in the current operation mode, send a result message + * to the client indicating we removed an element. + * + * @param op intersection operation + * @param element element to send + */ +static void +send_client_removed_element (struct Operation *op, + struct GNUNET_SET_Element *element) +{ + struct GNUNET_MQ_Envelope *ev; + struct GNUNET_SET_ResultMessage *rm; + + if (GNUNET_SET_RESULT_REMOVED != op->result_mode) + return; /* Wrong mode for transmitting removed elements */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending removed element (size %u) to client\n", + element->size); + GNUNET_STATISTICS_update (_GSS_statistics, + "# Element removed messages sent", + 1, + GNUNET_NO); + GNUNET_assert (0 != op->client_request_id); + ev = GNUNET_MQ_msg_extra (rm, + element->size, + GNUNET_MESSAGE_TYPE_SET_RESULT); + if (NULL == ev) + { + GNUNET_break (0); + return; + } + rm->result_status = htons (GNUNET_SET_STATUS_OK); + rm->request_id = htonl (op->client_request_id); + rm->element_type = element->element_type; + GNUNET_memcpy (&rm[1], + element->data, + element->size); + GNUNET_MQ_send (op->set->cs->mq, + ev); +} + + +/** + * Fills the "my_elements" hashmap with all relevant elements. + * + * @param cls the `struct Operation *` we are performing + * @param key current key code + * @param value the `struct ElementEntry *` from the hash map + * @return #GNUNET_YES (we should continue to iterate) + */ +static int +filtered_map_initialization (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct Operation *op = cls; + struct ElementEntry *ee = value; + struct GNUNET_HashCode mutated_hash; + + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "FIMA called for %s:%u\n", + GNUNET_h2s (&ee->element_hash), + ee->element.size); + + if (GNUNET_NO == _GSS_is_element_of_operation (ee, op)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Reduced initialization, not starting with %s:%u (wrong generation)\n", + GNUNET_h2s (&ee->element_hash), + ee->element.size); + return GNUNET_YES; /* element not valid in our operation's generation */ + } + + /* Test if element is in other peer's bloomfilter */ + GNUNET_BLOCK_mingle_hash (&ee->element_hash, + op->state->salt, + &mutated_hash); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Testing mingled hash %s with salt %u\n", + GNUNET_h2s (&mutated_hash), + op->state->salt); + if (GNUNET_NO == + GNUNET_CONTAINER_bloomfilter_test (op->state->remote_bf, + &mutated_hash)) + { + /* remove this element */ + send_client_removed_element (op, + &ee->element); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Reduced initialization, not starting with %s:%u\n", + GNUNET_h2s (&ee->element_hash), + ee->element.size); + return GNUNET_YES; + } + op->state->my_element_count++; + GNUNET_CRYPTO_hash_xor (&op->state->my_xor, + &ee->element_hash, + &op->state->my_xor); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Filtered initialization of my_elements, adding %s:%u\n", + GNUNET_h2s (&ee->element_hash), + ee->element.size); + GNUNET_break (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_put (op->state->my_elements, + &ee->element_hash, + ee, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + + return GNUNET_YES; +} + + +/** + * Removes elements from our hashmap if they are not contained within the + * provided remote bloomfilter. + * + * @param cls closure with the `struct Operation *` + * @param key current key code + * @param value value in the hash map + * @return #GNUNET_YES (we should continue to iterate) + */ +static int +iterator_bf_reduce (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct Operation *op = cls; + struct ElementEntry *ee = value; + struct GNUNET_HashCode mutated_hash; + + GNUNET_BLOCK_mingle_hash (&ee->element_hash, + op->state->salt, + &mutated_hash); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Testing mingled hash %s with salt %u\n", + GNUNET_h2s (&mutated_hash), + op->state->salt); + if (GNUNET_NO == + GNUNET_CONTAINER_bloomfilter_test (op->state->remote_bf, + &mutated_hash)) + { + GNUNET_break (0 < op->state->my_element_count); + op->state->my_element_count--; + GNUNET_CRYPTO_hash_xor (&op->state->my_xor, + &ee->element_hash, + &op->state->my_xor); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Bloom filter reduction of my_elements, removing %s:%u\n", + GNUNET_h2s (&ee->element_hash), + ee->element.size); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (op->state->my_elements, + &ee->element_hash, + ee)); + send_client_removed_element (op, + &ee->element); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Bloom filter reduction of my_elements, keeping %s:%u\n", + GNUNET_h2s (&ee->element_hash), + ee->element.size); + } + return GNUNET_YES; +} + + +/** + * Create initial bloomfilter based on all the elements given. + * + * @param cls the `struct Operation *` + * @param key current key code + * @param value the `struct ElementEntry` to process + * @return #GNUNET_YES (we should continue to iterate) + */ +static int +iterator_bf_create (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct Operation *op = cls; + struct ElementEntry *ee = value; + struct GNUNET_HashCode mutated_hash; + + GNUNET_BLOCK_mingle_hash (&ee->element_hash, + op->state->salt, + &mutated_hash); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Initializing BF with hash %s with salt %u\n", + GNUNET_h2s (&mutated_hash), + op->state->salt); + GNUNET_CONTAINER_bloomfilter_add (op->state->local_bf, + &mutated_hash); + return GNUNET_YES; +} + + +/** + * Inform the client that the intersection operation has failed, + * and proceed to destroy the evaluate operation. + * + * @param op the intersection operation to fail + */ +static void +fail_intersection_operation (struct Operation *op) +{ + struct GNUNET_MQ_Envelope *ev; + struct GNUNET_SET_ResultMessage *msg; + + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Intersection operation failed\n"); + GNUNET_STATISTICS_update (_GSS_statistics, + "# Intersection operations failed", + 1, + GNUNET_NO); + if (NULL != op->state->my_elements) + { + GNUNET_CONTAINER_multihashmap_destroy (op->state->my_elements); + op->state->my_elements = NULL; + } + ev = GNUNET_MQ_msg (msg, + GNUNET_MESSAGE_TYPE_SET_RESULT); + msg->result_status = htons (GNUNET_SET_STATUS_FAILURE); + msg->request_id = htonl (op->client_request_id); + msg->element_type = htons (0); + GNUNET_MQ_send (op->set->cs->mq, + ev); + _GSS_operation_destroy (op, + GNUNET_YES); +} + + +/** + * Send a bloomfilter to our peer. After the result done message has + * been sent to the client, destroy the evaluate operation. + * + * @param op intersection operation + */ +static void +send_bloomfilter (struct Operation *op) +{ + struct GNUNET_MQ_Envelope *ev; + struct BFMessage *msg; + uint32_t bf_size; + uint32_t bf_elementbits; + uint32_t chunk_size; + char *bf_data; + uint32_t offset; + + /* We consider the ratio of the set sizes to determine + the number of bits per element, as the smaller set + should use more bits to maximize its set reduction + potential and minimize overall bandwidth consumption. */ + bf_elementbits = 2 + ceil (log2 ((double) + (op->remote_element_count + / (double) op->state->my_element_count))); + if (bf_elementbits < 1) + bf_elementbits = 1; /* make sure k is not 0 */ + /* optimize BF-size to ~50% of bits set */ + bf_size = ceil ((double) (op->state->my_element_count + * bf_elementbits / log (2))); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending Bloom filter (%u) of size %u bytes\n", + (unsigned int) bf_elementbits, + (unsigned int) bf_size); + op->state->local_bf = GNUNET_CONTAINER_bloomfilter_init (NULL, + bf_size, + bf_elementbits); + op->state->salt = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, + UINT32_MAX); + GNUNET_CONTAINER_multihashmap_iterate (op->state->my_elements, + &iterator_bf_create, + op); + + /* send our Bloom filter */ + GNUNET_STATISTICS_update (_GSS_statistics, + "# Intersection Bloom filters sent", + 1, + GNUNET_NO); + chunk_size = 60 * 1024 - sizeof(struct BFMessage); + if (bf_size <= chunk_size) + { + /* singlepart */ + chunk_size = bf_size; + ev = GNUNET_MQ_msg_extra (msg, + chunk_size, + GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_BF); + GNUNET_assert (GNUNET_SYSERR != + GNUNET_CONTAINER_bloomfilter_get_raw_data ( + op->state->local_bf, + (char *) &msg[1], + bf_size)); + msg->sender_element_count = htonl (op->state->my_element_count); + msg->bloomfilter_total_length = htonl (bf_size); + msg->bits_per_element = htonl (bf_elementbits); + msg->sender_mutator = htonl (op->state->salt); + msg->element_xor_hash = op->state->my_xor; + GNUNET_MQ_send (op->mq, ev); + } + else + { + /* multipart */ + bf_data = GNUNET_malloc (bf_size); + GNUNET_assert (GNUNET_SYSERR != + GNUNET_CONTAINER_bloomfilter_get_raw_data ( + op->state->local_bf, + bf_data, + bf_size)); + offset = 0; + while (offset < bf_size) + { + if (bf_size - chunk_size < offset) + chunk_size = bf_size - offset; + ev = GNUNET_MQ_msg_extra (msg, + chunk_size, + GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_BF); + GNUNET_memcpy (&msg[1], + &bf_data[offset], + chunk_size); + offset += chunk_size; + msg->sender_element_count = htonl (op->state->my_element_count); + msg->bloomfilter_total_length = htonl (bf_size); + msg->bits_per_element = htonl (bf_elementbits); + msg->sender_mutator = htonl (op->state->salt); + msg->element_xor_hash = op->state->my_xor; + GNUNET_MQ_send (op->mq, ev); + } + GNUNET_free (bf_data); + } + GNUNET_CONTAINER_bloomfilter_free (op->state->local_bf); + op->state->local_bf = NULL; +} + + +/** + * Signal to the client that the operation has finished and + * destroy the operation. + * + * @param cls operation to destroy + */ +static void +send_client_done_and_destroy (void *cls) +{ + struct Operation *op = cls; + struct GNUNET_MQ_Envelope *ev; + struct GNUNET_SET_ResultMessage *rm; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Intersection succeeded, sending DONE to local client\n"); + GNUNET_STATISTICS_update (_GSS_statistics, + "# Intersection operations succeeded", + 1, + GNUNET_NO); + ev = GNUNET_MQ_msg (rm, + GNUNET_MESSAGE_TYPE_SET_RESULT); + rm->request_id = htonl (op->client_request_id); + rm->result_status = htons (GNUNET_SET_STATUS_DONE); + rm->element_type = htons (0); + GNUNET_MQ_send (op->set->cs->mq, + ev); + _GSS_operation_destroy (op, + GNUNET_YES); +} + + +/** + * Remember that we are done dealing with the local client + * AND have sent the other peer our message that we are done, + * so we are not just waiting for the channel to die before + * telling the local client that we are done as our last act. + * + * @param cls the `struct Operation`. + */ +static void +finished_local_operations (void *cls) +{ + struct Operation *op = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "DONE sent to other peer, now waiting for other end to close the channel\n"); + op->state->phase = PHASE_FINISHED; + op->state->channel_death_expected = GNUNET_YES; +} + + +/** + * Notify the other peer that we are done. Once this message + * is out, we still need to notify the local client that we + * are done. + * + * @param op operation to notify for. + */ +static void +send_p2p_done (struct Operation *op) +{ + struct GNUNET_MQ_Envelope *ev; + struct IntersectionDoneMessage *idm; + + GNUNET_assert (PHASE_MUST_SEND_DONE == op->state->phase); + GNUNET_assert (GNUNET_NO == op->state->channel_death_expected); + ev = GNUNET_MQ_msg (idm, + GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_DONE); + idm->final_element_count = htonl (op->state->my_element_count); + idm->element_xor_hash = op->state->my_xor; + GNUNET_MQ_notify_sent (ev, + &finished_local_operations, + op); + GNUNET_MQ_send (op->mq, + ev); +} + + +/** + * Send all elements in the full result iterator. + * + * @param cls the `struct Operation *` + */ +static void +send_remaining_elements (void *cls) +{ + struct Operation *op = cls; + const void *nxt; + const struct ElementEntry *ee; + struct GNUNET_MQ_Envelope *ev; + struct GNUNET_SET_ResultMessage *rm; + const struct GNUNET_SET_Element *element; + int res; + + res = GNUNET_CONTAINER_multihashmap_iterator_next ( + op->state->full_result_iter, + NULL, + &nxt); + if (GNUNET_NO == res) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending done and destroy because iterator ran out\n"); + GNUNET_CONTAINER_multihashmap_iterator_destroy ( + op->state->full_result_iter); + op->state->full_result_iter = NULL; + if (PHASE_DONE_RECEIVED == op->state->phase) + { + op->state->phase = PHASE_FINISHED; + send_client_done_and_destroy (op); + } + else if (PHASE_MUST_SEND_DONE == op->state->phase) + { + send_p2p_done (op); + } + else + { + GNUNET_assert (0); + } + return; + } + ee = nxt; + element = &ee->element; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending element %s:%u to client (full set)\n", + GNUNET_h2s (&ee->element_hash), + element->size); + GNUNET_assert (0 != op->client_request_id); + ev = GNUNET_MQ_msg_extra (rm, + element->size, + GNUNET_MESSAGE_TYPE_SET_RESULT); + GNUNET_assert (NULL != ev); + rm->result_status = htons (GNUNET_SET_STATUS_OK); + rm->request_id = htonl (op->client_request_id); + rm->element_type = element->element_type; + GNUNET_memcpy (&rm[1], + element->data, + element->size); + GNUNET_MQ_notify_sent (ev, + &send_remaining_elements, + op); + GNUNET_MQ_send (op->set->cs->mq, + ev); +} + + +/** + * Fills the "my_elements" hashmap with the initial set of + * (non-deleted) elements from the set of the specification. + * + * @param cls closure with the `struct Operation *` + * @param key current key code for the element + * @param value value in the hash map with the `struct ElementEntry *` + * @return #GNUNET_YES (we should continue to iterate) + */ +static int +initialize_map_unfiltered (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct ElementEntry *ee = value; + struct Operation *op = cls; + + if (GNUNET_NO == _GSS_is_element_of_operation (ee, op)) + return GNUNET_YES; /* element not live in operation's generation */ + GNUNET_CRYPTO_hash_xor (&op->state->my_xor, + &ee->element_hash, + &op->state->my_xor); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Initial full initialization of my_elements, adding %s:%u\n", + GNUNET_h2s (&ee->element_hash), + ee->element.size); + GNUNET_break (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_put (op->state->my_elements, + &ee->element_hash, + ee, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + return GNUNET_YES; +} + + +/** + * Send our element count to the peer, in case our element count is + * lower than theirs. + * + * @param op intersection operation + */ +static void +send_element_count (struct Operation *op) +{ + struct GNUNET_MQ_Envelope *ev; + struct IntersectionElementInfoMessage *msg; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending our element count (%u)\n", + op->state->my_element_count); + ev = GNUNET_MQ_msg (msg, + GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_ELEMENT_INFO); + msg->sender_element_count = htonl (op->state->my_element_count); + GNUNET_MQ_send (op->mq, ev); +} + + +/** + * We go first, initialize our map with all elements and + * send the first Bloom filter. + * + * @param op operation to start exchange for + */ +static void +begin_bf_exchange (struct Operation *op) +{ + op->state->phase = PHASE_BF_EXCHANGE; + GNUNET_CONTAINER_multihashmap_iterate (op->set->content->elements, + &initialize_map_unfiltered, + op); + send_bloomfilter (op); +} + + +/** + * Handle the initial `struct IntersectionElementInfoMessage` from a + * remote peer. + * + * @param cls the intersection operation + * @param mh the header of the message + */ +void +handle_intersection_p2p_element_info (void *cls, + const struct + IntersectionElementInfoMessage *msg) +{ + struct Operation *op = cls; + + if (GNUNET_SET_OPERATION_INTERSECTION != op->set->operation) + { + GNUNET_break_op (0); + fail_intersection_operation (op); + return; + } + op->remote_element_count = ntohl (msg->sender_element_count); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received remote element count (%u), I have %u\n", + op->remote_element_count, + op->state->my_element_count); + if (((PHASE_INITIAL != op->state->phase) && + (PHASE_COUNT_SENT != op->state->phase)) || + (op->state->my_element_count > op->remote_element_count) || + (0 == op->state->my_element_count) || + (0 == op->remote_element_count)) + { + GNUNET_break_op (0); + fail_intersection_operation (op); + return; + } + GNUNET_break (NULL == op->state->remote_bf); + begin_bf_exchange (op); + GNUNET_CADET_receive_done (op->channel); +} + + +/** + * Process a Bloomfilter once we got all the chunks. + * + * @param op the intersection operation + */ +static void +process_bf (struct Operation *op) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received BF in phase %u, foreign count is %u, my element count is %u/%u\n", + op->state->phase, + op->remote_element_count, + op->state->my_element_count, + GNUNET_CONTAINER_multihashmap_size (op->set->content->elements)); + switch (op->state->phase) + { + case PHASE_INITIAL: + GNUNET_break_op (0); + fail_intersection_operation (op); + return; + + case PHASE_COUNT_SENT: + /* This is the first BF being sent, build our initial map with + filtering in place */ + op->state->my_element_count = 0; + GNUNET_CONTAINER_multihashmap_iterate (op->set->content->elements, + &filtered_map_initialization, + op); + break; + + case PHASE_BF_EXCHANGE: + /* Update our set by reduction */ + GNUNET_CONTAINER_multihashmap_iterate (op->state->my_elements, + &iterator_bf_reduce, + op); + break; + + case PHASE_MUST_SEND_DONE: + GNUNET_break_op (0); + fail_intersection_operation (op); + return; + + case PHASE_DONE_RECEIVED: + GNUNET_break_op (0); + fail_intersection_operation (op); + return; + + case PHASE_FINISHED: + GNUNET_break_op (0); + fail_intersection_operation (op); + return; + } + GNUNET_CONTAINER_bloomfilter_free (op->state->remote_bf); + op->state->remote_bf = NULL; + + if ((0 == op->state->my_element_count) || /* fully disjoint */ + ((op->state->my_element_count == op->remote_element_count) && + (0 == GNUNET_memcmp (&op->state->my_xor, + &op->state->other_xor)))) + { + /* we are done */ + op->state->phase = PHASE_MUST_SEND_DONE; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Intersection succeeded, sending DONE to other peer\n"); + GNUNET_CONTAINER_bloomfilter_free (op->state->local_bf); + op->state->local_bf = NULL; + if (GNUNET_SET_RESULT_FULL == op->result_mode) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending full result set (%u elements)\n", + GNUNET_CONTAINER_multihashmap_size (op->state->my_elements)); + op->state->full_result_iter + = GNUNET_CONTAINER_multihashmap_iterator_create ( + op->state->my_elements); + send_remaining_elements (op); + return; + } + send_p2p_done (op); + return; + } + op->state->phase = PHASE_BF_EXCHANGE; + send_bloomfilter (op); +} + + +/** + * Check an BF message from a remote peer. + * + * @param cls the intersection operation + * @param msg the header of the message + * @return #GNUNET_OK if @a msg is well-formed + */ +static int +check_intersection_p2p_bf (void *cls, + const struct BFMessage *msg) +{ + struct Operation *op = cls; + + if (GNUNET_SET_OPERATION_INTERSECTION != op->set->operation) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Handle an BF message from a remote peer. + * + * @param cls the intersection operation + * @param msg the header of the message + */ +static +handle_intersection_p2p_bf (void *cls, + const struct BFMessage *msg) +{ + struct Operation *op = cls; + uint32_t bf_size; + uint32_t chunk_size; + uint32_t bf_bits_per_element; + + switch (op->state->phase) + { + case PHASE_INITIAL: + GNUNET_break_op (0); + fail_intersection_operation (op); + return; + + case PHASE_COUNT_SENT: + case PHASE_BF_EXCHANGE: + bf_size = ntohl (msg->bloomfilter_total_length); + bf_bits_per_element = ntohl (msg->bits_per_element); + chunk_size = htons (msg->header.size) - sizeof(struct BFMessage); + op->state->other_xor = msg->element_xor_hash; + if (bf_size == chunk_size) + { + if (NULL != op->state->bf_data) + { + GNUNET_break_op (0); + fail_intersection_operation (op); + return; + } + /* single part, done here immediately */ + op->state->remote_bf + = GNUNET_CONTAINER_bloomfilter_init ((const char *) &msg[1], + bf_size, + bf_bits_per_element); + op->state->salt = ntohl (msg->sender_mutator); + op->remote_element_count = ntohl (msg->sender_element_count); + process_bf (op); + break; + } + /* multipart chunk */ + if (NULL == op->state->bf_data) + { + /* first chunk, initialize */ + op->state->bf_data = GNUNET_malloc (bf_size); + op->state->bf_data_size = bf_size; + op->state->bf_bits_per_element = bf_bits_per_element; + op->state->bf_data_offset = 0; + op->state->salt = ntohl (msg->sender_mutator); + op->remote_element_count = ntohl (msg->sender_element_count); + } + else + { + /* increment */ + if ((op->state->bf_data_size != bf_size) || + (op->state->bf_bits_per_element != bf_bits_per_element) || + (op->state->bf_data_offset + chunk_size > bf_size) || + (op->state->salt != ntohl (msg->sender_mutator)) || + (op->remote_element_count != ntohl (msg->sender_element_count))) + { + GNUNET_break_op (0); + fail_intersection_operation (op); + return; + } + } + GNUNET_memcpy (&op->state->bf_data[op->state->bf_data_offset], + (const char *) &msg[1], + chunk_size); + op->state->bf_data_offset += chunk_size; + if (op->state->bf_data_offset == bf_size) + { + /* last chunk, run! */ + op->state->remote_bf + = GNUNET_CONTAINER_bloomfilter_init (op->state->bf_data, + bf_size, + bf_bits_per_element); + GNUNET_free (op->state->bf_data); + op->state->bf_data = NULL; + op->state->bf_data_size = 0; + process_bf (op); + } + break; + + default: + GNUNET_break_op (0); + fail_intersection_operation (op); + return; + } + GNUNET_CADET_receive_done (op->channel); +} + + +/** + * Remove all elements from our hashmap. + * + * @param cls closure with the `struct Operation *` + * @param key current key code + * @param value value in the hash map + * @return #GNUNET_YES (we should continue to iterate) + */ +static int +filter_all (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct Operation *op = cls; + struct ElementEntry *ee = value; + + GNUNET_break (0 < op->state->my_element_count); + op->state->my_element_count--; + GNUNET_CRYPTO_hash_xor (&op->state->my_xor, + &ee->element_hash, + &op->state->my_xor); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Final reduction of my_elements, removing %s:%u\n", + GNUNET_h2s (&ee->element_hash), + ee->element.size); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (op->state->my_elements, + &ee->element_hash, + ee)); + send_client_removed_element (op, + &ee->element); + return GNUNET_YES; +} + + +/** + * Handle a done message from a remote peer + * + * @param cls the intersection operation + * @param mh the message + */ +static void +handle_intersection_p2p_done (void *cls, + const struct IntersectionDoneMessage *idm) +{ + struct Operation *op = cls; + + if (GNUNET_SET_OPERATION_INTERSECTION != op->set->operation) + { + GNUNET_break_op (0); + fail_intersection_operation (op); + return; + } + if (PHASE_BF_EXCHANGE != op->state->phase) + { + /* wrong phase to conclude? FIXME: Or should we allow this + if the other peer has _initially_ already an empty set? */ + GNUNET_break_op (0); + fail_intersection_operation (op); + return; + } + if (0 == ntohl (idm->final_element_count)) + { + /* other peer determined empty set is the intersection, + remove all elements */ + GNUNET_CONTAINER_multihashmap_iterate (op->state->my_elements, + &filter_all, + op); + } + if ((op->state->my_element_count != ntohl (idm->final_element_count)) || + (0 != GNUNET_memcmp (&op->state->my_xor, + &idm->element_xor_hash))) + { + /* Other peer thinks we are done, but we disagree on the result! */ + GNUNET_break_op (0); + fail_intersection_operation (op); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Got IntersectionDoneMessage, have %u elements in intersection\n", + op->state->my_element_count); + op->state->phase = PHASE_DONE_RECEIVED; + GNUNET_CADET_receive_done (op->channel); + + GNUNET_assert (GNUNET_NO == op->state->client_done_sent); + if (GNUNET_SET_RESULT_FULL == op->result_mode) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending full result set to client (%u elements)\n", + GNUNET_CONTAINER_multihashmap_size (op->state->my_elements)); + op->state->full_result_iter + = GNUNET_CONTAINER_multihashmap_iterator_create (op->state->my_elements); + send_remaining_elements (op); + return; + } + op->state->phase = PHASE_FINISHED; + send_client_done_and_destroy (op); +} + + +/** + * Initiate a set intersection operation with a remote peer. + * + * @param op operation that is created, should be initialized to + * begin the evaluation + * @param opaque_context message to be transmitted to the listener + * to convince it to accept, may be NULL + * @return operation-specific state to keep in @a op + */ +static struct OperationState * +intersection_evaluate (struct Operation *op, + const struct GNUNET_MessageHeader *opaque_context) +{ + struct OperationState *state; + struct GNUNET_MQ_Envelope *ev; + struct OperationRequestMessage *msg; + + ev = GNUNET_MQ_msg_nested_mh (msg, + GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST, + opaque_context); + if (NULL == ev) + { + /* the context message is too large!? */ + GNUNET_break (0); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Initiating intersection operation evaluation\n"); + state = GNUNET_new (struct OperationState); + /* we started the operation, thus we have to send the operation request */ + state->phase = PHASE_INITIAL; + state->my_element_count = op->set->state->current_set_element_count; + state->my_elements + = GNUNET_CONTAINER_multihashmap_create (state->my_element_count, + GNUNET_YES); + + msg->operation = htonl (GNUNET_SET_OPERATION_INTERSECTION); + msg->element_count = htonl (state->my_element_count); + GNUNET_MQ_send (op->mq, + ev); + state->phase = PHASE_COUNT_SENT; + if (NULL != opaque_context) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sent op request with context message\n"); + else + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sent op request without context message\n"); + return state; +} + + +/** + * Accept an intersection operation request from a remote peer. Only + * initializes the private operation state. + * + * @param op operation that will be accepted as an intersection operation + */ +static struct OperationState * +intersection_accept (struct Operation *op) +{ + struct OperationState *state; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Accepting set intersection operation\n"); + state = GNUNET_new (struct OperationState); + state->phase = PHASE_INITIAL; + state->my_element_count + = op->set->state->current_set_element_count; + state->my_elements + = GNUNET_CONTAINER_multihashmap_create (GNUNET_MIN (state->my_element_count, + op->remote_element_count), + GNUNET_YES); + op->state = state; + if (op->remote_element_count < state->my_element_count) + { + /* If the other peer (Alice) has fewer elements than us (Bob), + we just send the count as Alice should send the first BF */ + send_element_count (op); + state->phase = PHASE_COUNT_SENT; + return state; + } + /* We have fewer elements, so we start with the BF */ + begin_bf_exchange (op); + return state; +} + + +/** + * Destroy the intersection operation. Only things specific to the + * intersection operation are destroyed. + * + * @param op intersection operation to destroy + */ +static void +intersection_op_cancel (struct Operation *op) +{ + /* check if the op was canceled twice */ + GNUNET_assert (NULL != op->state); + if (NULL != op->state->remote_bf) + { + GNUNET_CONTAINER_bloomfilter_free (op->state->remote_bf); + op->state->remote_bf = NULL; + } + if (NULL != op->state->local_bf) + { + GNUNET_CONTAINER_bloomfilter_free (op->state->local_bf); + op->state->local_bf = NULL; + } + if (NULL != op->state->my_elements) + { + GNUNET_CONTAINER_multihashmap_destroy (op->state->my_elements); + op->state->my_elements = NULL; + } + if (NULL != op->state->full_result_iter) + { + GNUNET_CONTAINER_multihashmap_iterator_destroy ( + op->state->full_result_iter); + op->state->full_result_iter = NULL; + } + GNUNET_free (op->state); + op->state = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Destroying intersection op state done\n"); +} + + +/** + * Create a new set supporting the intersection operation. + * + * @return the newly created set + */ +static struct SetState * +intersection_set_create () +{ + struct SetState *set_state; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Intersection set created\n"); + set_state = GNUNET_new (struct SetState); + set_state->current_set_element_count = 0; + + return set_state; +} + + +/** + * Add the element from the given element message to the set. + * + * @param set_state state of the set want to add to + * @param ee the element to add to the set + */ +static void +intersection_add (struct SetState *set_state, + struct ElementEntry *ee) +{ + set_state->current_set_element_count++; +} + + +/** + * Destroy a set that supports the intersection operation + * + * @param set_state the set to destroy + */ +static void +intersection_set_destroy (struct SetState *set_state) +{ + GNUNET_free (set_state); +} + + +/** + * Remove the element given in the element message from the set. + * + * @param set_state state of the set to remove from + * @param element set element to remove + */ +static void +intersection_remove (struct SetState *set_state, + struct ElementEntry *element) +{ + GNUNET_assert (0 < set_state->current_set_element_count); + set_state->current_set_element_count--; +} + + +/** + * Callback for channel death for the intersection operation. + * + * @param op operation that lost the channel + */ +static void +intersection_channel_death (struct Operation *op) +{ + if (GNUNET_YES == op->state->channel_death_expected) + { + /* oh goodie, we are done! */ + send_client_done_and_destroy (op); + } + else + { + /* sorry, channel went down early, too bad. */ + _GSS_operation_destroy (op, + GNUNET_YES); + } +} + + +/** + * Get the incoming socket associated with the given id. + * + * @param listener the listener to look in + * @param id id to look for + * @return the incoming socket associated with the id, + * or NULL if there is none + */ +static struct Operation * +get_incoming (uint32_t id) +{ + for (struct Listener *listener = listener_head; NULL != listener; + listener = listener->next) + { + for (struct Operation *op = listener->op_head; NULL != op; op = op->next) + if (op->suggest_id == id) + return op; + } + return NULL; +} + + +/** + * Destroy an incoming request from a remote peer + * + * @param op remote request to destroy + */ +static void +incoming_destroy (struct Operation *op) +{ + struct Listener *listener; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Destroying incoming operation %p\n", + op); + if (NULL != (listener = op->listener)) + { + GNUNET_CONTAINER_DLL_remove (listener->op_head, listener->op_tail, op); + op->listener = NULL; + } + if (NULL != op->timeout_task) + { + GNUNET_SCHEDULER_cancel (op->timeout_task); + op->timeout_task = NULL; + } + _GSS_operation_destroy2 (op); +} + + +/** + * Context for the #garbage_collect_cb(). + */ +struct GarbageContext +{ + /** + * Map for which we are garbage collecting removed elements. + */ + struct GNUNET_CONTAINER_MultiHashMap *map; + + /** + * Lowest generation for which an operation is still pending. + */ + unsigned int min_op_generation; + + /** + * Largest generation for which an operation is still pending. + */ + unsigned int max_op_generation; +}; + + +/** + * Function invoked to check if an element can be removed from + * the set's history because it is no longer needed. + * + * @param cls the `struct GarbageContext *` + * @param key key of the element in the map + * @param value the `struct ElementEntry *` + * @return #GNUNET_OK (continue to iterate) + */ +static int +garbage_collect_cb (void *cls, const struct GNUNET_HashCode *key, void *value) +{ + // struct GarbageContext *gc = cls; + // struct ElementEntry *ee = value; + + // if (GNUNET_YES != ee->removed) + // return GNUNET_OK; + // if ( (gc->max_op_generation < ee->generation_added) || + // (ee->generation_removed > gc->min_op_generation) ) + // { + // GNUNET_assert (GNUNET_YES == + // GNUNET_CONTAINER_multihashmap_remove (gc->map, + // key, + // ee)); + // GNUNET_free (ee); + // } + return GNUNET_OK; +} + + +/** + * Collect and destroy elements that are not needed anymore, because + * their lifetime (as determined by their generation) does not overlap + * with any active set operation. + * + * @param set set to garbage collect + */ +static void +collect_generation_garbage (struct Set *set) +{ + struct GarbageContext gc; + + gc.min_op_generation = UINT_MAX; + gc.max_op_generation = 0; + for (struct Operation *op = set->ops_head; NULL != op; op = op->next) + { + gc.min_op_generation = + GNUNET_MIN (gc.min_op_generation, op->generation_created); + gc.max_op_generation = + GNUNET_MAX (gc.max_op_generation, op->generation_created); + } + gc.map = set->content->elements; + GNUNET_CONTAINER_multihashmap_iterate (set->content->elements, + &garbage_collect_cb, + &gc); +} + + +/** + * Is @a generation in the range of exclusions? + * + * @param generation generation to query + * @param excluded array of generations where the element is excluded + * @param excluded_size length of the @a excluded array + * @return #GNUNET_YES if @a generation is in any of the ranges + */ +static int +is_excluded_generation (unsigned int generation, + struct GenerationRange *excluded, + unsigned int excluded_size) +{ + for (unsigned int i = 0; i < excluded_size; i++) + if ((generation >= excluded[i].start) && (generation < excluded[i].end)) + return GNUNET_YES; + return GNUNET_NO; +} + + +/** + * Is element @a ee part of the set during @a query_generation? + * + * @param ee element to test + * @param query_generation generation to query + * @param excluded array of generations where the element is excluded + * @param excluded_size length of the @a excluded array + * @return #GNUNET_YES if the element is in the set, #GNUNET_NO if not + */ +static int +is_element_of_generation (struct ElementEntry *ee, + unsigned int query_generation, + struct GenerationRange *excluded, + unsigned int excluded_size) +{ + struct MutationEvent *mut; + int is_present; + + GNUNET_assert (NULL != ee->mutations); + if (GNUNET_YES == + is_excluded_generation (query_generation, excluded, excluded_size)) + { + GNUNET_break (0); + return GNUNET_NO; + } + + is_present = GNUNET_NO; + + /* Could be made faster with binary search, but lists + are small, so why bother. */ + for (unsigned int i = 0; i < ee->mutations_size; i++) + { + mut = &ee->mutations[i]; + + if (mut->generation > query_generation) + { + /* The mutation doesn't apply to our generation + anymore. We can'b break here, since mutations aren't + sorted by generation. */ + continue; + } + + if (GNUNET_YES == + is_excluded_generation (mut->generation, excluded, excluded_size)) + { + /* The generation is excluded (because it belongs to another + fork via a lazy copy) and thus mutations aren't considered + for membership testing. */ + continue; + } + + /* This would be an inconsistency in how we manage mutations. */ + if ((GNUNET_YES == is_present) && (GNUNET_YES == mut->added)) + GNUNET_assert (0); + /* Likewise. */ + if ((GNUNET_NO == is_present) && (GNUNET_NO == mut->added)) + GNUNET_assert (0); + + is_present = mut->added; + } + + return is_present; +} + + +/** + * Is element @a ee part of the set used by @a op? + * + * @param ee element to test + * @param op operation the defines the set and its generation + * @return #GNUNET_YES if the element is in the set, #GNUNET_NO if not + */ +int +_GSS_is_element_of_operation (struct ElementEntry *ee, struct Operation *op) +{ + return is_element_of_generation (ee, + op->generation_created, + op->set->excluded_generations, + op->set->excluded_generations_size); +} + + +/** + * Destroy the given operation. Used for any operation where both + * peers were known and that thus actually had a vt and channel. Must + * not be used for operations where 'listener' is still set and we do + * not know the other peer. + * + * Call the implementation-specific cancel function of the operation. + * Disconnects from the remote peer. Does not disconnect the client, + * as there may be multiple operations per set. + * + * @param op operation to destroy + * @param gc #GNUNET_YES to perform garbage collection on the set + */ +void +_GSS_operation_destroy (struct Operation *op, int gc) +{ + struct Set *set = op->set; + struct GNUNET_CADET_Channel *channel; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Destroying operation %p\n", op); + GNUNET_assert (NULL == op->listener); + if (NULL != op->state) + { + intersection_cancel (op); // FIXME: inline + op->state = NULL; + } + if (NULL != set) + { + GNUNET_CONTAINER_DLL_remove (set->ops_head, set->ops_tail, op); + op->set = NULL; + } + if (NULL != op->context_msg) + { + GNUNET_free (op->context_msg); + op->context_msg = NULL; + } + if (NULL != (channel = op->channel)) + { + /* This will free op; called conditionally as this helper function + is also called from within the channel disconnect handler. */ + op->channel = NULL; + GNUNET_CADET_channel_destroy (channel); + } + if ((NULL != set) && (GNUNET_YES == gc)) + collect_generation_garbage (set); + /* We rely on the channel end handler to free 'op'. When 'op->channel' was NULL, + * there was a channel end handler that will free 'op' on the call stack. */ +} + + +/** + * Callback called when a client connects to the service. + * + * @param cls closure for the service + * @param c the new client that connected to the service + * @param mq the message queue used to send messages to the client + * @return @a `struct ClientState` + */ +static void * +client_connect_cb (void *cls, + struct GNUNET_SERVICE_Client *c, + struct GNUNET_MQ_Handle *mq) +{ + struct ClientState *cs; + + num_clients++; + cs = GNUNET_new (struct ClientState); + cs->client = c; + cs->mq = mq; + return cs; +} + + +/** + * Iterator over hash map entries to free element entries. + * + * @param cls closure + * @param key current key code + * @param value a `struct ElementEntry *` to be free'd + * @return #GNUNET_YES (continue to iterate) + */ +static int +destroy_elements_iterator (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct ElementEntry *ee = value; + + GNUNET_free (ee->mutations); + GNUNET_free (ee); + return GNUNET_YES; +} + + +/** + * Clean up after a client has disconnected + * + * @param cls closure, unused + * @param client the client to clean up after + * @param internal_cls the `struct ClientState` + */ +static void +client_disconnect_cb (void *cls, + struct GNUNET_SERVICE_Client *client, + void *internal_cls) +{ + struct ClientState *cs = internal_cls; + struct Operation *op; + struct Listener *listener; + struct Set *set; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client disconnected, cleaning up\n"); + if (NULL != (set = cs->set)) + { + struct SetContent *content = set->content; + struct PendingMutation *pm; + struct PendingMutation *pm_current; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Destroying client's set\n"); + /* Destroy pending set operations */ + while (NULL != set->ops_head) + _GSS_operation_destroy (set->ops_head, GNUNET_NO); + + /* Destroy operation-specific state */ + GNUNET_assert (NULL != set->state); + intersection_set_destroy (set->state); // FIXME: inline + set->state = NULL; + + /* Clean up ongoing iterations */ + if (NULL != set->iter) + { + GNUNET_CONTAINER_multihashmap_iterator_destroy (set->iter); + set->iter = NULL; + set->iteration_id++; + } + + /* discard any pending mutations that reference this set */ + pm = content->pending_mutations_head; + while (NULL != pm) + { + pm_current = pm; + pm = pm->next; + if (pm_current->set == set) + { + GNUNET_CONTAINER_DLL_remove (content->pending_mutations_head, + content->pending_mutations_tail, + pm_current); + GNUNET_free (pm_current); + } + } + + /* free set content (or at least decrement RC) */ + set->content = NULL; + GNUNET_assert (0 != content->refcount); + content->refcount--; + if (0 == content->refcount) + { + GNUNET_assert (NULL != content->elements); + GNUNET_CONTAINER_multihashmap_iterate (content->elements, + &destroy_elements_iterator, + NULL); + GNUNET_CONTAINER_multihashmap_destroy (content->elements); + content->elements = NULL; + GNUNET_free (content); + } + GNUNET_free (set->excluded_generations); + set->excluded_generations = NULL; + + GNUNET_free (set); + } + + if (NULL != (listener = cs->listener)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Destroying client's listener\n"); + GNUNET_CADET_close_port (listener->open_port); + listener->open_port = NULL; + while (NULL != (op = listener->op_head)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Destroying incoming operation `%u' from peer `%s'\n", + (unsigned int) op->client_request_id, + GNUNET_i2s (&op->peer)); + incoming_destroy (op); + } + GNUNET_CONTAINER_DLL_remove (listener_head, listener_tail, listener); + GNUNET_free (listener); + } + GNUNET_free (cs); + num_clients--; + if ((GNUNET_YES == in_shutdown) && (0 == num_clients)) + { + if (NULL != cadet) + { + GNUNET_CADET_disconnect (cadet); + cadet = NULL; + } + } +} + + +/** + * Check a request for a set operation from another peer. + * + * @param cls the operation state + * @param msg the received message + * @return #GNUNET_OK if the channel should be kept alive, + * #GNUNET_SYSERR to destroy the channel + */ +static int +check_incoming_msg (void *cls, const struct OperationRequestMessage *msg) +{ + struct Operation *op = cls; + struct Listener *listener = op->listener; + const struct GNUNET_MessageHeader *nested_context; + + /* double operation request */ + if (0 != op->suggest_id) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + /* This should be equivalent to the previous condition, but can't hurt to check twice */ + if (NULL == op->listener) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (listener->operation != + (enum GNUNET_SET_OperationType) ntohl (msg->operation)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + nested_context = GNUNET_MQ_extract_nested_mh (msg); + if ((NULL != nested_context) && + (ntohs (nested_context->size) > GNUNET_SET_CONTEXT_MESSAGE_MAX_SIZE)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Handle a request for a set operation from another peer. Checks if we + * have a listener waiting for such a request (and in that case initiates + * asking the listener about accepting the connection). If no listener + * is waiting, we queue the operation request in hope that a listener + * shows up soon (before timeout). + * + * This msg is expected as the first and only msg handled through the + * non-operation bound virtual table, acceptance of this operation replaces + * our virtual table and subsequent msgs would be routed differently (as + * we then know what type of operation this is). + * + * @param cls the operation state + * @param msg the received message + * @return #GNUNET_OK if the channel should be kept alive, + * #GNUNET_SYSERR to destroy the channel + */ +static void +handle_incoming_msg (void *cls, const struct OperationRequestMessage *msg) +{ + struct Operation *op = cls; + struct Listener *listener = op->listener; + const struct GNUNET_MessageHeader *nested_context; + struct GNUNET_MQ_Envelope *env; + struct GNUNET_SET_RequestMessage *cmsg; + + nested_context = GNUNET_MQ_extract_nested_mh (msg); + /* Make a copy of the nested_context (application-specific context + information that is opaque to set) so we can pass it to the + listener later on */ + if (NULL != nested_context) + op->context_msg = GNUNET_copy_message (nested_context); + op->remote_element_count = ntohl (msg->element_count); + GNUNET_log ( + GNUNET_ERROR_TYPE_DEBUG, + "Received P2P operation request (op %u, port %s) for active listener\n", + (uint32_t) ntohl (msg->operation), + GNUNET_h2s (&op->listener->app_id)); + GNUNET_assert (0 == op->suggest_id); + if (0 == suggest_id) + suggest_id++; + op->suggest_id = suggest_id++; + GNUNET_assert (NULL != op->timeout_task); + GNUNET_SCHEDULER_cancel (op->timeout_task); + op->timeout_task = NULL; + env = GNUNET_MQ_msg_nested_mh (cmsg, + GNUNET_MESSAGE_TYPE_SETI_REQUEST, + op->context_msg); + GNUNET_log ( + GNUNET_ERROR_TYPE_DEBUG, + "Suggesting incoming request with accept id %u to listener %p of client %p\n", + op->suggest_id, + listener, + listener->cs); + cmsg->accept_id = htonl (op->suggest_id); + cmsg->peer_id = op->peer; + GNUNET_MQ_send (listener->cs->mq, env); + /* NOTE: GNUNET_CADET_receive_done() will be called in + #handle_client_accept() */ +} + + +/** + * Add an element to @a set as specified by @a msg + * + * @param set set to manipulate + * @param msg message specifying the change + */ +static void +execute_add (struct Set *set, const struct GNUNET_SET_ElementMessage *msg) +{ + struct GNUNET_SET_Element el; + struct ElementEntry *ee; + struct GNUNET_HashCode hash; + + GNUNET_assert (GNUNET_MESSAGE_TYPE_SETI_ADD == ntohs (msg->header.type)); + el.size = ntohs (msg->header.size) - sizeof(*msg); + el.data = &msg[1]; + el.element_type = ntohs (msg->element_type); + GNUNET_SET_element_hash (&el, &hash); + ee = GNUNET_CONTAINER_multihashmap_get (set->content->elements, &hash); + if (NULL == ee) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client inserts element %s of size %u\n", + GNUNET_h2s (&hash), + el.size); + ee = GNUNET_malloc (el.size + sizeof(*ee)); + ee->element.size = el.size; + GNUNET_memcpy (&ee[1], el.data, el.size); + ee->element.data = &ee[1]; + ee->element.element_type = el.element_type; + ee->remote = GNUNET_NO; + ee->mutations = NULL; + ee->mutations_size = 0; + ee->element_hash = hash; + GNUNET_break (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_put ( + set->content->elements, + &ee->element_hash, + ee, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + } + else if (GNUNET_YES == + is_element_of_generation (ee, + set->current_generation, + set->excluded_generations, + set->excluded_generations_size)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client inserted element %s of size %u twice (ignored)\n", + GNUNET_h2s (&hash), + el.size); + + /* same element inserted twice */ + return; + } + + { + struct MutationEvent mut = { .generation = set->current_generation, + .added = GNUNET_YES }; + GNUNET_array_append (ee->mutations, ee->mutations_size, mut); + } + // FIXME: inline + intersection_add (set->state, + ee); +} + + +/** + * Perform a mutation on a set as specified by the @a msg + * + * @param set the set to mutate + * @param msg specification of what to change + */ +static void +execute_mutation (struct Set *set, const struct GNUNET_SET_ElementMessage *msg) +{ + switch (ntohs (msg->header.type)) + { + case GNUNET_MESSAGE_TYPE_SETI_ADD: // FIXME: inline! + execute_add (set, msg); + break; + default: + GNUNET_break (0); + } +} + + +/** + * Execute mutations that were delayed on a set because of + * pending operations. + * + * @param set the set to execute mutations on + */ +static void +execute_delayed_mutations (struct Set *set) +{ + struct PendingMutation *pm; + + if (0 != set->content->iterator_count) + return; /* still cannot do this */ + while (NULL != (pm = set->content->pending_mutations_head)) + { + GNUNET_CONTAINER_DLL_remove (set->content->pending_mutations_head, + set->content->pending_mutations_tail, + pm); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Executing pending mutation on %p.\n", + pm->set); + execute_mutation (pm->set, pm->msg); + GNUNET_free (pm->msg); + GNUNET_free (pm); + } +} + + +/** + * Send the next element of a set to the set's client. The next element is given by + * the set's current hashmap iterator. The set's iterator will be set to NULL if there + * are no more elements in the set. The caller must ensure that the set's iterator is + * valid. + * + * The client will acknowledge each received element with a + * #GNUNET_MESSAGE_TYPE_SETI_ITER_ACK message. Our + * #handle_client_iter_ack() will then trigger the next transmission. + * Note that the #GNUNET_MESSAGE_TYPE_SETI_ITER_DONE is not acknowledged. + * + * @param set set that should send its next element to its client + */ +static void +send_client_element (struct Set *set) +{ + int ret; + struct ElementEntry *ee; + struct GNUNET_MQ_Envelope *ev; + struct GNUNET_SET_IterResponseMessage *msg; + + GNUNET_assert (NULL != set->iter); + do + { + ret = GNUNET_CONTAINER_multihashmap_iterator_next (set->iter, + NULL, + (const void **) &ee); + if (GNUNET_NO == ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Iteration on %p done.\n", set); + ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SETI_ITER_DONE); + GNUNET_CONTAINER_multihashmap_iterator_destroy (set->iter); + set->iter = NULL; + set->iteration_id++; + GNUNET_assert (set->content->iterator_count > 0); + set->content->iterator_count--; + execute_delayed_mutations (set); + GNUNET_MQ_send (set->cs->mq, ev); + return; + } + GNUNET_assert (NULL != ee); + } + while (GNUNET_NO == + is_element_of_generation (ee, + set->iter_generation, + set->excluded_generations, + set->excluded_generations_size)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending iteration element on %p.\n", + set); + ev = GNUNET_MQ_msg_extra (msg, + ee->element.size, + GNUNET_MESSAGE_TYPE_SETI_ITER_ELEMENT); + GNUNET_memcpy (&msg[1], ee->element.data, ee->element.size); + msg->element_type = htons (ee->element.element_type); + msg->iteration_id = htons (set->iteration_id); + GNUNET_MQ_send (set->cs->mq, ev); +} + + +/** + * Called when a client wants to iterate the elements of a set. + * Checks if we have a set associated with the client and if we + * can right now start an iteration. If all checks out, starts + * sending the elements of the set to the client. + * + * @param cls client that sent the message + * @param m message sent by the client + */ +static void +handle_client_iterate (void *cls, const struct GNUNET_MessageHeader *m) +{ + struct ClientState *cs = cls; + struct Set *set; + + if (NULL == (set = cs->set)) + { + /* attempt to iterate over a non existing set */ + GNUNET_break (0); + GNUNET_SERVICE_client_drop (cs->client); + return; + } + if (NULL != set->iter) + { + /* Only one concurrent iterate-action allowed per set */ + GNUNET_break (0); + GNUNET_SERVICE_client_drop (cs->client); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Iterating set %p in gen %u with %u content elements\n", + (void *) set, + set->current_generation, + GNUNET_CONTAINER_multihashmap_size (set->content->elements)); + GNUNET_SERVICE_client_continue (cs->client); + set->content->iterator_count++; + set->iter = + GNUNET_CONTAINER_multihashmap_iterator_create (set->content->elements); + set->iter_generation = set->current_generation; + send_client_element (set); +} + + +/** + * Called when a client wants to create a new set. This is typically + * the first request from a client, and includes the type of set + * operation to be performed. + * + * @param cls client that sent the message + * @param m message sent by the client + */ +static void +handle_client_create_set (void *cls, const struct GNUNET_SET_CreateMessage *msg) +{ + struct ClientState *cs = cls; + struct Set *set; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client created new set (operation %u)\n", + (uint32_t) ntohl (msg->operation)); + if (NULL != cs->set) + { + /* There can only be one set per client */ + GNUNET_break (0); + GNUNET_SERVICE_client_drop (cs->client); + return; + } + set = GNUNET_new (struct Set); + switch (ntohl (msg->operation)) + { + case GNUNET_SET_OPERATION_INTERSECTION: + set->vt = _GSS_intersection_vt (); + break; + + case GNUNET_SET_OPERATION_UNION: + set->vt = _GSS_union_vt (); + break; + + default: + GNUNET_free (set); + GNUNET_break (0); + GNUNET_SERVICE_client_drop (cs->client); + return; + } + set->operation = (enum GNUNET_SET_OperationType) ntohl (msg->operation); + set->state = intersection_set_create (); // FIXME: inline + if (NULL == set->state) + { + /* initialization failed (i.e. out of memory) */ + GNUNET_free (set); + GNUNET_SERVICE_client_drop (cs->client); + return; + } + set->content = GNUNET_new (struct SetContent); + set->content->refcount = 1; + set->content->elements = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES); + set->cs = cs; + cs->set = set; + GNUNET_SERVICE_client_continue (cs->client); +} + + +/** + * Timeout happens iff: + * - we suggested an operation to our listener, + * but did not receive a response in time + * - we got the channel from a peer but no #GNUNET_MESSAGE_TYPE_SETI_P2P_OPERATION_REQUEST + * + * @param cls channel context + * @param tc context information (why was this task triggered now) + */ +static void +incoming_timeout_cb (void *cls) +{ + struct Operation *op = cls; + + op->timeout_task = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Remote peer's incoming request timed out\n"); + incoming_destroy (op); +} + + +/** + * Method called whenever another peer has added us to a channel the + * other peer initiated. Only called (once) upon reception of data + * from a channel we listen on. + * + * The channel context represents the operation itself and gets added + * to a DLL, from where it gets looked up when our local listener + * client responds to a proposed/suggested operation or connects and + * associates with this operation. + * + * @param cls closure + * @param channel new handle to the channel + * @param source peer that started the channel + * @return initial channel context for the channel + * returns NULL on error + */ +static void * +channel_new_cb (void *cls, + struct GNUNET_CADET_Channel *channel, + const struct GNUNET_PeerIdentity *source) +{ + struct Listener *listener = cls; + struct Operation *op; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New incoming channel\n"); + op = GNUNET_new (struct Operation); + op->listener = listener; + op->peer = *source; + op->channel = channel; + op->mq = GNUNET_CADET_get_mq (op->channel); + op->salt = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX); + op->timeout_task = GNUNET_SCHEDULER_add_delayed (INCOMING_CHANNEL_TIMEOUT, + &incoming_timeout_cb, + op); + GNUNET_CONTAINER_DLL_insert (listener->op_head, listener->op_tail, op); + return op; +} + + +/** + * Function called whenever a channel is destroyed. Should clean up + * any associated state. It must NOT call + * GNUNET_CADET_channel_destroy() on the channel. + * + * The peer_disconnect function is part of a a virtual table set initially either + * when a peer creates a new channel with us, or once we create + * a new channel ourselves (evaluate). + * + * Once we know the exact type of operation (union/intersection), the vt is + * replaced with an operation specific instance (_GSS_[op]_vt). + * + * @param channel_ctx place where local state associated + * with the channel is stored + * @param channel connection to the other end (henceforth invalid) + */ +static void +channel_end_cb (void *channel_ctx, const struct GNUNET_CADET_Channel *channel) +{ + struct Operation *op = channel_ctx; + + op->channel = NULL; + _GSS_operation_destroy2 (op); +} + + +/** + * This function probably should not exist + * and be replaced by inlining more specific + * logic in the various places where it is called. + */ +void +_GSS_operation_destroy2 (struct Operation *op) +{ + struct GNUNET_CADET_Channel *channel; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "channel_end_cb called\n"); + if (NULL != (channel = op->channel)) + { + /* This will free op; called conditionally as this helper function + is also called from within the channel disconnect handler. */ + op->channel = NULL; + GNUNET_CADET_channel_destroy (channel); + } + if (NULL != op->listener) + { + incoming_destroy (op); + return; + } + if (NULL != op->set) + intersection_channel_death (op); // FIXME: inline + else + _GSS_operation_destroy (op, GNUNET_YES); + GNUNET_free (op); +} + + +/** + * Function called whenever an MQ-channel's transmission window size changes. + * + * The first callback in an outgoing channel will be with a non-zero value + * and will mean the channel is connected to the destination. + * + * For an incoming channel it will be called immediately after the + * #GNUNET_CADET_ConnectEventHandler, also with a non-zero value. + * + * @param cls Channel closure. + * @param channel Connection to the other end (henceforth invalid). + * @param window_size New window size. If the is more messages than buffer size + * this value will be negative.. + */ +static void +channel_window_cb (void *cls, + const struct GNUNET_CADET_Channel *channel, + int window_size) +{ + /* FIXME: not implemented, we could do flow control here... */ +} + + +/** + * Called when a client wants to create a new listener. + * + * @param cls client that sent the message + * @param msg message sent by the client + */ +static void +handle_client_listen (void *cls, const struct GNUNET_SET_ListenMessage *msg) +{ + struct ClientState *cs = cls; + struct GNUNET_MQ_MessageHandler cadet_handlers[] = + { GNUNET_MQ_hd_var_size (incoming_msg, + GNUNET_MESSAGE_TYPE_SETI_P2P_OPERATION_REQUEST, + struct OperationRequestMessage, + NULL), + GNUNET_MQ_hd_fixed_size (intersection_p2p_element_info, + GNUNET_MESSAGE_TYPE_SETI_P2P_ELEMENT_INFO, + struct IntersectionElementInfoMessage, + NULL), + GNUNET_MQ_hd_var_size (intersection_p2p_bf, + GNUNET_MESSAGE_TYPE_SETI_P2P_BF, + struct BFMessage, + NULL), + GNUNET_MQ_hd_fixed_size (intersection_p2p_done, + GNUNET_MESSAGE_TYPE_SETI_P2P_DONE, + struct IntersectionDoneMessage, + NULL), + GNUNET_MQ_handler_end () }; + struct Listener *listener; + + if (NULL != cs->listener) + { + /* max. one active listener per client! */ + GNUNET_break (0); + GNUNET_SERVICE_client_drop (cs->client); + return; + } + listener = GNUNET_new (struct Listener); + listener->cs = cs; + cs->listener = listener; + listener->app_id = msg->app_id; + listener->operation = (enum GNUNET_SET_OperationType) ntohl (msg->operation); + GNUNET_CONTAINER_DLL_insert (listener_head, listener_tail, listener); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "New listener created (op %u, port %s)\n", + listener->operation, + GNUNET_h2s (&listener->app_id)); + listener->open_port = GNUNET_CADET_open_port (cadet, + &msg->app_id, + &channel_new_cb, + listener, + &channel_window_cb, + &channel_end_cb, + cadet_handlers); + GNUNET_SERVICE_client_continue (cs->client); +} + + +/** + * Called when the listening client rejects an operation + * request by another peer. + * + * @param cls client that sent the message + * @param msg message sent by the client + */ +static void +handle_client_reject (void *cls, const struct GNUNET_SET_RejectMessage *msg) +{ + struct ClientState *cs = cls; + struct Operation *op; + + op = get_incoming (ntohl (msg->accept_reject_id)); + if (NULL == op) + { + /* no matching incoming operation for this reject; + could be that the other peer already disconnected... */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Client rejected unknown operation %u\n", + (unsigned int) ntohl (msg->accept_reject_id)); + GNUNET_SERVICE_client_continue (cs->client); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Peer request (op %u, app %s) rejected by client\n", + op->listener->operation, + GNUNET_h2s (&cs->listener->app_id)); + _GSS_operation_destroy2 (op); + GNUNET_SERVICE_client_continue (cs->client); +} + + +/** + * Called when a client wants to add or remove an element to a set it inhabits. + * + * @param cls client that sent the message + * @param msg message sent by the client + */ +static int +check_client_mutation (void *cls, const struct GNUNET_SET_ElementMessage *msg) +{ + /* NOTE: Technically, we should probably check with the + block library whether the element we are given is well-formed */ + return GNUNET_OK; +} + + +/** + * Called when a client wants to add or remove an element to a set it inhabits. + * + * @param cls client that sent the message + * @param msg message sent by the client + */ +static void +handle_client_mutation (void *cls, const struct GNUNET_SET_ElementMessage *msg) +{ + struct ClientState *cs = cls; + struct Set *set; + + if (NULL == (set = cs->set)) + { + /* client without a set requested an operation */ + GNUNET_break (0); + GNUNET_SERVICE_client_drop (cs->client); + return; + } + GNUNET_SERVICE_client_continue (cs->client); + + if (0 != set->content->iterator_count) + { + struct PendingMutation *pm; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Scheduling mutation on set\n"); + pm = GNUNET_new (struct PendingMutation); + pm->msg = + (struct GNUNET_SET_ElementMessage *) GNUNET_copy_message (&msg->header); + pm->set = set; + GNUNET_CONTAINER_DLL_insert_tail (set->content->pending_mutations_head, + set->content->pending_mutations_tail, + pm); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Executing mutation on set\n"); + execute_mutation (set, msg); +} + + +/** + * Advance the current generation of a set, + * adding exclusion ranges if necessary. + * + * @param set the set where we want to advance the generation + */ +static void +advance_generation (struct Set *set) +{ + struct GenerationRange r; + + if (set->current_generation == set->content->latest_generation) + { + set->content->latest_generation++; + set->current_generation++; + return; + } + + GNUNET_assert (set->current_generation < set->content->latest_generation); + + r.start = set->current_generation + 1; + r.end = set->content->latest_generation + 1; + set->content->latest_generation = r.end; + set->current_generation = r.end; + GNUNET_array_append (set->excluded_generations, + set->excluded_generations_size, + r); +} + + +/** + * Called when a client wants to initiate a set operation with another + * peer. Initiates the CADET connection to the listener and sends the + * request. + * + * @param cls client that sent the message + * @param msg message sent by the client + * @return #GNUNET_OK if the message is well-formed + */ +static int +check_client_evaluate (void *cls, const struct GNUNET_SET_EvaluateMessage *msg) +{ + /* FIXME: suboptimal, even if the context below could be NULL, + there are malformed messages this does not check for... */ + return GNUNET_OK; +} + + +/** + * Called when a client wants to initiate a set operation with another + * peer. Initiates the CADET connection to the listener and sends the + * request. + * + * @param cls client that sent the message + * @param msg message sent by the client + */ +static void +handle_client_evaluate (void *cls, const struct GNUNET_SET_EvaluateMessage *msg) +{ + struct ClientState *cs = cls; + struct Operation *op = GNUNET_new (struct Operation); + const struct GNUNET_MQ_MessageHandler cadet_handlers[] = { + GNUNET_MQ_hd_var_size (incoming_msg, + GNUNET_MESSAGE_TYPE_SETI_P2P_OPERATION_REQUEST, + struct OperationRequestMessage, + op), + GNUNET_MQ_hd_fixed_size (intersection_p2p_element_info, + GNUNET_MESSAGE_TYPE_SETI_P2P_ELEMENT_INFO, + struct IntersectionElementInfoMessage, + op), + GNUNET_MQ_hd_var_size (intersection_p2p_bf, + GNUNET_MESSAGE_TYPE_SETI_P2P_BF, + struct BFMessage, + op), + GNUNET_MQ_hd_fixed_size (intersection_p2p_done, + GNUNET_MESSAGE_TYPE_SETI_P2P_DONE, + struct IntersectionDoneMessage, + op), + GNUNET_MQ_handler_end () + }; + struct Set *set; + const struct GNUNET_MessageHeader *context; + + if (NULL == (set = cs->set)) + { + GNUNET_break (0); + GNUNET_free (op); + GNUNET_SERVICE_client_drop (cs->client); + return; + } + op->salt = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX); + op->peer = msg->target_peer; + op->result_mode = ntohl (msg->result_mode); + op->client_request_id = ntohl (msg->request_id); + op->byzantine = msg->byzantine; + op->byzantine_lower_bound = msg->byzantine_lower_bound; + op->force_full = msg->force_full; + op->force_delta = msg->force_delta; + context = GNUNET_MQ_extract_nested_mh (msg); + + /* Advance generation values, so that + mutations won't interfer with the running operation. */ + op->set = set; + op->generation_created = set->current_generation; + advance_generation (set); + GNUNET_CONTAINER_DLL_insert (set->ops_head, set->ops_tail, op); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Creating new CADET channel to port %s for set operation type %u\n", + GNUNET_h2s (&msg->app_id), + set->operation); + op->channel = GNUNET_CADET_channel_create (cadet, + op, + &msg->target_peer, + &msg->app_id, + &channel_window_cb, + &channel_end_cb, + cadet_handlers); + op->mq = GNUNET_CADET_get_mq (op->channel); + op->state = intersection_evaluate (op, context); // FIXME: inline! + if (NULL == op->state) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (cs->client); + return; + } + GNUNET_SERVICE_client_continue (cs->client); +} + + +/** + * Handle a request from the client to cancel a running set operation. + * + * @param cls the client + * @param msg the message + */ +static void +handle_client_cancel (void *cls, const struct GNUNET_SET_CancelMessage *msg) +{ + struct ClientState *cs = cls; + struct Set *set; + struct Operation *op; + int found; + + if (NULL == (set = cs->set)) + { + /* client without a set requested an operation */ + GNUNET_break (0); + GNUNET_SERVICE_client_drop (cs->client); + return; + } + found = GNUNET_NO; + for (op = set->ops_head; NULL != op; op = op->next) + { + if (op->client_request_id == ntohl (msg->request_id)) + { + found = GNUNET_YES; + break; + } + } + if (GNUNET_NO == found) + { + /* It may happen that the operation was already destroyed due to + * the other peer disconnecting. The client may not know about this + * yet and try to cancel the (just barely non-existent) operation. + * So this is not a hard error. + */GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Client canceled non-existent op %u\n", + (uint32_t) ntohl (msg->request_id)); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client requested cancel for op %u\n", + (uint32_t) ntohl (msg->request_id)); + _GSS_operation_destroy (op, GNUNET_YES); + } + GNUNET_SERVICE_client_continue (cs->client); +} + + +/** + * Handle a request from the client to accept a set operation that + * came from a remote peer. We forward the accept to the associated + * operation for handling + * + * @param cls the client + * @param msg the message + */ +static void +handle_client_accept (void *cls, const struct GNUNET_SET_AcceptMessage *msg) +{ + struct ClientState *cs = cls; + struct Set *set; + struct Operation *op; + struct GNUNET_SET_ResultMessage *result_message; + struct GNUNET_MQ_Envelope *ev; + struct Listener *listener; + + if (NULL == (set = cs->set)) + { + /* client without a set requested to accept */ + GNUNET_break (0); + GNUNET_SERVICE_client_drop (cs->client); + return; + } + op = get_incoming (ntohl (msg->accept_reject_id)); + if (NULL == op) + { + /* It is not an error if the set op does not exist -- it may + * have been destroyed when the partner peer disconnected. */ + GNUNET_log ( + GNUNET_ERROR_TYPE_INFO, + "Client %p accepted request %u of listener %p that is no longer active\n", + cs, + ntohl (msg->accept_reject_id), + cs->listener); + ev = GNUNET_MQ_msg (result_message, GNUNET_MESSAGE_TYPE_SETI_RESULT); + result_message->request_id = msg->request_id; + result_message->result_status = htons (GNUNET_SET_STATUS_FAILURE); + GNUNET_MQ_send (set->cs->mq, ev); + GNUNET_SERVICE_client_continue (cs->client); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client accepting request %u\n", + (uint32_t) ntohl (msg->accept_reject_id)); + listener = op->listener; + op->listener = NULL; + GNUNET_CONTAINER_DLL_remove (listener->op_head, listener->op_tail, op); + op->set = set; + GNUNET_CONTAINER_DLL_insert (set->ops_head, set->ops_tail, op); + op->client_request_id = ntohl (msg->request_id); + op->result_mode = ntohl (msg->result_mode); + op->byzantine = msg->byzantine; + op->byzantine_lower_bound = msg->byzantine_lower_bound; + op->force_full = msg->force_full; + op->force_delta = msg->force_delta; + + /* Advance generation values, so that future mutations do not + interfer with the running operation. */ + op->generation_created = set->current_generation; + advance_generation (set); + GNUNET_assert (NULL == op->state); + op->state = intersection_accept (op); // FIXME: inline + if (NULL == op->state) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (cs->client); + return; + } + /* Now allow CADET to continue, as we did not do this in + #handle_incoming_msg (as we wanted to first see if the + local client would accept the request). */ + GNUNET_CADET_receive_done (op->channel); + GNUNET_SERVICE_client_continue (cs->client); +} + + +/** + * Called to clean up, after a shutdown has been requested. + * + * @param cls closure, NULL + */ +static void +shutdown_task (void *cls) +{ + /* Delay actual shutdown to allow service to disconnect clients */ + in_shutdown = GNUNET_YES; + if (0 == num_clients) + { + if (NULL != cadet) + { + GNUNET_CADET_disconnect (cadet); + cadet = NULL; + } + } + GNUNET_STATISTICS_destroy (_GSS_statistics, GNUNET_YES); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "handled shutdown request\n"); +} + + +/** + * Function called by the service's run + * method to run service-specific setup code. + * + * @param cls closure + * @param cfg configuration to use + * @param service the initialized service + */ +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_SERVICE_Handle *service) +{ + /* FIXME: need to modify SERVICE (!) API to allow + us to run a shutdown task *after* clients were + forcefully disconnected! */ + GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); + _GSS_statistics = GNUNET_STATISTICS_create ("set", cfg); + cadet = GNUNET_CADET_connect (cfg); + if (NULL == cadet) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Could not connect to CADET service\n")); + GNUNET_SCHEDULER_shutdown (); + return; + } +} + + +/** + * Define "main" method using service macro. + */ +GNUNET_SERVICE_MAIN ( + "set", + GNUNET_SERVICE_OPTION_NONE, + &run, + &client_connect_cb, + &client_disconnect_cb, + NULL, + GNUNET_MQ_hd_fixed_size (client_accept, + GNUNET_MESSAGE_TYPE_SETI_ACCEPT, + struct GNUNET_SET_AcceptMessage, + NULL), + GNUNET_MQ_hd_var_size (client_mutation, + GNUNET_MESSAGE_TYPE_SETI_ADD, + struct GNUNET_SET_ElementMessage, + NULL), + GNUNET_MQ_hd_fixed_size (client_create_set, + GNUNET_MESSAGE_TYPE_SETI_CREATE, + struct GNUNET_SET_CreateMessage, + NULL), + GNUNET_MQ_hd_var_size (client_evaluate, + GNUNET_MESSAGE_TYPE_SETI_EVALUATE, + struct GNUNET_SET_EvaluateMessage, + NULL), + GNUNET_MQ_hd_fixed_size (client_listen, + GNUNET_MESSAGE_TYPE_SETI_LISTEN, + struct GNUNET_SET_ListenMessage, + NULL), + GNUNET_MQ_hd_fixed_size (client_reject, + GNUNET_MESSAGE_TYPE_SETI_REJECT, + struct GNUNET_SET_RejectMessage, + NULL), + GNUNET_MQ_hd_fixed_size (client_cancel, + GNUNET_MESSAGE_TYPE_SETI_CANCEL, + struct GNUNET_SET_CancelMessage, + NULL), + GNUNET_MQ_handler_end ()); + + +/* end of gnunet-service-seti.c */ diff --git a/src/seti/gnunet-service-seti_protocol.h b/src/seti/gnunet-service-seti_protocol.h new file mode 100644 index 000000000..51968376e --- /dev/null +++ b/src/seti/gnunet-service-seti_protocol.h @@ -0,0 +1,144 @@ +/* + This file is part of GNUnet. + Copyright (C) 2013, 2014, 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Florian Dold + * @author Christian Grothoff + * @file seti/gnunet-service-seti_protocol.h + * @brief Peer-to-Peer messages for gnunet set + */ +#ifndef SETI_PROTOCOL_H +#define SETI_PROTOCOL_H + +#include "platform.h" +#include "gnunet_common.h" + + +GNUNET_NETWORK_STRUCT_BEGIN + +struct OperationRequestMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST + */ + struct GNUNET_MessageHeader header; + + /** + * For Intersection: my element count + */ + uint32_t element_count GNUNET_PACKED; + + /** + * Application-specific identifier of the request. + */ + struct GNUNET_HashCode app_idX; + + /* rest: optional message */ +}; + + +/** + * During intersection, the first (and possibly second) message + * send it the number of elements in the set, to allow the peers + * to decide who should start with the Bloom filter. + */ +struct IntersectionElementInfoMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_ELEMENT_INFO + */ + struct GNUNET_MessageHeader header; + + /** + * mutator used with this bloomfilter. + */ + uint32_t sender_element_count GNUNET_PACKED; +}; + + +/** + * Bloom filter messages exchanged for set intersection calculation. + */ +struct BFMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_BF + */ + struct GNUNET_MessageHeader header; + + /** + * Number of elements the sender still has in the set. + */ + uint32_t sender_element_count GNUNET_PACKED; + + /** + * XOR of all hashes over all elements remaining in the set. + * Used to determine termination. + */ + struct GNUNET_HashCode element_xor_hash; + + /** + * Mutator used with this bloomfilter. + */ + uint32_t sender_mutator GNUNET_PACKED; + + /** + * Total length of the bloomfilter data. + */ + uint32_t bloomfilter_total_length GNUNET_PACKED; + + /** + * Number of bits (k-value) used in encoding the bloomfilter. + */ + uint32_t bits_per_element GNUNET_PACKED; + + /** + * rest: the sender's bloomfilter + */ +}; + + +/** + * Last message, send to confirm the final set. Contains the element + * count as it is possible that the peer determined that we were done + * by getting the empty set, which in that case also needs to be + * communicated. + */ +struct IntersectionDoneMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_DONE + */ + struct GNUNET_MessageHeader header; + + /** + * Final number of elements in intersection. + */ + uint32_t final_element_count GNUNET_PACKED; + + /** + * XOR of all hashes over all elements remaining in the set. + */ + struct GNUNET_HashCode element_xor_hash; +}; + + +GNUNET_NETWORK_STRUCT_END + +#endif diff --git a/src/seti/gnunet-seti-profiler.c b/src/seti/gnunet-seti-profiler.c new file mode 100644 index 000000000..b8230bcfc --- /dev/null +++ b/src/seti/gnunet-seti-profiler.c @@ -0,0 +1,480 @@ +/* + This file is part of GNUnet + Copyright (C) 2013, 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file set/gnunet-seti-profiler.c + * @brief profiling tool for set intersection + * @author Florian Dold + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_statistics_service.h" +#include "gnunet_seti_service.h" +#include "gnunet_testbed_service.h" + + +static int ret; + +static unsigned int num_a = 5; +static unsigned int num_b = 5; +static unsigned int num_c = 20; + +const static struct GNUNET_CONFIGURATION_Handle *config; + +struct SetInfo +{ + char *id; + struct GNUNET_SETI_Handle *set; + struct GNUNET_SETI_OperationHandle *oh; + struct GNUNET_CONTAINER_MultiHashMap *sent; + struct GNUNET_CONTAINER_MultiHashMap *received; + int done; +} info1, info2; + +static struct GNUNET_CONTAINER_MultiHashMap *common_sent; + +static struct GNUNET_HashCode app_id; + +static struct GNUNET_PeerIdentity local_peer; + +static struct GNUNET_SETI_ListenHandle *set_listener; + +static unsigned int use_intersection; + +static unsigned int element_size = 32; + +/** + * Handle to the statistics service. + */ +static struct GNUNET_STATISTICS_Handle *statistics; + +/** + * The profiler will write statistics + * for all peers to the file with this name. + */ +static char *statistics_filename; + +/** + * The profiler will write statistics + * for all peers to this file. + */ +static FILE *statistics_file; + + +static int +map_remove_iterator (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct GNUNET_CONTAINER_MultiHashMap *m = cls; + int ret; + + GNUNET_assert (NULL != key); + + ret = GNUNET_CONTAINER_multihashmap_remove_all (m, key); + if (GNUNET_OK != ret) + printf ("spurious element\n"); + return GNUNET_YES; +} + + +/** + * Callback function to process statistic values. + * + * @param cls closure + * @param subsystem name of subsystem that created the statistic + * @param name the name of the datum + * @param value the current value + * @param is_persistent #GNUNET_YES if the value is persistent, #GNUNET_NO if not + * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration + */ +static int +statistics_result (void *cls, + const char *subsystem, + const char *name, + uint64_t value, + int is_persistent) +{ + if (NULL != statistics_file) + { + fprintf (statistics_file, "%s\t%s\t%lu\n", subsystem, name, (unsigned + long) value); + } + return GNUNET_OK; +} + + +static void +statistics_done (void *cls, + int success) +{ + GNUNET_assert (GNUNET_YES == success); + if (NULL != statistics_file) + fclose (statistics_file); + GNUNET_SCHEDULER_shutdown (); +} + + +static void +check_all_done (void) +{ + if ((info1.done == GNUNET_NO) || (info2.done == GNUNET_NO)) + return; + + GNUNET_CONTAINER_multihashmap_iterate (info1.received, map_remove_iterator, + info2.sent); + GNUNET_CONTAINER_multihashmap_iterate (info2.received, map_remove_iterator, + info1.sent); + + printf ("set a: %d missing elements\n", GNUNET_CONTAINER_multihashmap_size ( + info1.sent)); + printf ("set b: %d missing elements\n", GNUNET_CONTAINER_multihashmap_size ( + info2.sent)); + + if (NULL == statistics_filename) + { + GNUNET_SCHEDULER_shutdown (); + return; + } + + statistics_file = fopen (statistics_filename, "w"); + GNUNET_STATISTICS_get (statistics, NULL, NULL, + &statistics_done, + &statistics_result, NULL); +} + + +static void +set_result_cb (void *cls, + const struct GNUNET_SETI_Element *element, + uint64_t current_size, + enum GNUNET_SETI_Status status) +{ + struct SetInfo *info = cls; + struct GNUNET_HashCode hash; + + GNUNET_assert (GNUNET_NO == info->done); + switch (status) + { + case GNUNET_SETI_STATUS_DONE: + info->done = GNUNET_YES; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "set intersection done\n"); + check_all_done (); + info->oh = NULL; + return; + case GNUNET_SETI_STATUS_FAILURE: + info->oh = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "failure\n"); + GNUNET_SCHEDULER_shutdown (); + return; + case GNUNET_SETI_STATUS_ADD_LOCAL: + GNUNET_CRYPTO_hash (element->data, + element->size, + &hash); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "set %s: keep element %s\n", + info->id, + GNUNET_h2s (&hash)); + break; + case GNUNET_SETI_STATUS_DEL_LOCAL: + GNUNET_CRYPTO_hash (element->data, + element->size, + &hash); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "set %s: remove element %s\n", + info->id, + GNUNET_h2s (&hash)); + return; + default: + GNUNET_assert (0); + } + + if (element->size != element_size) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "wrong element size: %u, expected %u\n", + element->size, + (unsigned int) sizeof(struct GNUNET_HashCode)); + GNUNET_assert (0); + } + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "set %s: got element (%s)\n", + info->id, GNUNET_h2s (element->data)); + GNUNET_assert (NULL != element->data); + { + struct GNUNET_HashCode data_hash; + + GNUNET_CRYPTO_hash (element->data, + element_size, + &data_hash); + GNUNET_CONTAINER_multihashmap_put (info->received, + &data_hash, + NULL, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE); + } +} + + +static void +set_listen_cb (void *cls, + const struct GNUNET_PeerIdentity *other_peer, + const struct GNUNET_MessageHeader *context_msg, + struct GNUNET_SETI_Request *request) +{ + /* max. 1 option plus terminator */ + struct GNUNET_SETI_Option opts[2] = { { 0 } }; + unsigned int n_opts = 0; + + if (NULL == request) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "listener failed\n"); + return; + } + GNUNET_assert (NULL == info2.oh); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "set listen cb called\n"); + if (use_intersection) + { + opts[n_opts++] = (struct GNUNET_SETI_Option) { .type = + GNUNET_SETI_OPTION_RETURN_INTERSECTION }; + } + opts[n_opts].type = GNUNET_SETI_OPTION_END; + info2.oh = GNUNET_SETI_accept (request, + opts, + &set_result_cb, + &info2); + GNUNET_SETI_commit (info2.oh, + info2.set); +} + + +static int +set_insert_iterator (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct GNUNET_SETI_Handle *set = cls; + struct GNUNET_SETI_Element el; + + el.element_type = 0; + el.data = value; + el.size = element_size; + GNUNET_SETI_add_element (set, &el, NULL, NULL); + return GNUNET_YES; +} + + +static void +handle_shutdown (void *cls) +{ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Shutting down set profiler\n"); + if (NULL != set_listener) + { + GNUNET_SETI_listen_cancel (set_listener); + set_listener = NULL; + } + if (NULL != info1.oh) + { + GNUNET_SETI_operation_cancel (info1.oh); + info1.oh = NULL; + } + if (NULL != info2.oh) + { + GNUNET_SETI_operation_cancel (info2.oh); + info2.oh = NULL; + } + if (NULL != info1.set) + { + GNUNET_SETI_destroy (info1.set); + info1.set = NULL; + } + if (NULL != info2.set) + { + GNUNET_SETI_destroy (info2.set); + info2.set = NULL; + } + GNUNET_STATISTICS_destroy (statistics, GNUNET_NO); +} + + +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + unsigned int i; + struct GNUNET_HashCode hash; + /* max. 1 option plus terminator */ + struct GNUNET_SETI_Option opts[2] = { { 0 } }; + unsigned int n_opts = 0; + + config = cfg; + + GNUNET_assert (element_size > 0); + + if (GNUNET_OK != GNUNET_CRYPTO_get_peer_identity (cfg, &local_peer)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "could not retrieve host identity\n"); + ret = 0; + return; + } + statistics = GNUNET_STATISTICS_create ("set-profiler", cfg); + GNUNET_SCHEDULER_add_shutdown (&handle_shutdown, NULL); + info1.id = "a"; + info2.id = "b"; + info1.sent = GNUNET_CONTAINER_multihashmap_create (num_a + 1, GNUNET_NO); + info2.sent = GNUNET_CONTAINER_multihashmap_create (num_b + 1, GNUNET_NO); + common_sent = GNUNET_CONTAINER_multihashmap_create (num_c + 1, GNUNET_NO); + info1.received = GNUNET_CONTAINER_multihashmap_create (num_a + 1, GNUNET_NO); + info2.received = GNUNET_CONTAINER_multihashmap_create (num_b + 1, GNUNET_NO); + for (i = 0; i < num_a; i++) + { + char *data = GNUNET_malloc (element_size); + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, data, element_size); + GNUNET_CRYPTO_hash (data, element_size, &hash); + GNUNET_CONTAINER_multihashmap_put (info1.sent, &hash, data, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE); + } + + for (i = 0; i < num_b; i++) + { + char *data = GNUNET_malloc (element_size); + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, data, element_size); + GNUNET_CRYPTO_hash (data, element_size, &hash); + GNUNET_CONTAINER_multihashmap_put (info2.sent, &hash, data, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE); + } + + for (i = 0; i < num_c; i++) + { + char *data = GNUNET_malloc (element_size); + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, data, element_size); + GNUNET_CRYPTO_hash (data, element_size, &hash); + GNUNET_CONTAINER_multihashmap_put (common_sent, &hash, data, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE); + } + + GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_STRONG, &app_id); + + info1.set = GNUNET_SETI_create (config); + info2.set = GNUNET_SETI_create (config); + GNUNET_CONTAINER_multihashmap_iterate (info1.sent, + &set_insert_iterator, + info1.set); + GNUNET_CONTAINER_multihashmap_iterate (info2.sent, + &set_insert_iterator, + info2.set); + GNUNET_CONTAINER_multihashmap_iterate (common_sent, + &set_insert_iterator, + info1.set); + GNUNET_CONTAINER_multihashmap_iterate (common_sent, + &set_insert_iterator, + info2.set); + + set_listener = GNUNET_SETI_listen (config, + &app_id, + &set_listen_cb, + NULL); + if (use_intersection) + { + opts[n_opts++] = (struct GNUNET_SETI_Option) { .type = + GNUNET_SETI_OPTION_RETURN_INTERSECTION }; + } + opts[n_opts].type = GNUNET_SETI_OPTION_END; + + info1.oh = GNUNET_SETI_prepare (&local_peer, + &app_id, + NULL, + opts, + set_result_cb, + &info1); + GNUNET_SETI_commit (info1.oh, + info1.set); + GNUNET_SETI_destroy (info1.set); + info1.set = NULL; +} + + +static void +pre_run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + if (0 != GNUNET_TESTING_peer_run ("set-profiler", + cfgfile, + &run, NULL)) + ret = 2; +} + + +int +main (int argc, char **argv) +{ + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_option_uint ('A', + "num-first", + NULL, + gettext_noop ("number of values"), + &num_a), + GNUNET_GETOPT_option_uint ('B', + "num-second", + NULL, + gettext_noop ("number of values"), + &num_b), + GNUNET_GETOPT_option_uint ('C', + "num-common", + NULL, + gettext_noop ("number of values"), + &num_c), + GNUNET_GETOPT_option_uint ('i', + "use-intersection", + NULL, + gettext_noop ( + "return intersection instead of delta"), + &use_intersection), + GNUNET_GETOPT_option_uint ('w', + "element-size", + NULL, + gettext_noop ("element size"), + &element_size), + GNUNET_GETOPT_option_filename ('s', + "statistics", + "FILENAME", + gettext_noop ("write statistics to file"), + &statistics_filename), + GNUNET_GETOPT_OPTION_END + }; + + GNUNET_PROGRAM_run2 (argc, argv, + "gnunet-seti-profiler", + "help", + options, + &pre_run, + NULL, + GNUNET_YES); + return ret; +} diff --git a/src/seti/plugin_block_seti_test.c b/src/seti/plugin_block_seti_test.c new file mode 100644 index 000000000..1de086092 --- /dev/null +++ b/src/seti/plugin_block_seti_test.c @@ -0,0 +1,123 @@ +/* + This file is part of GNUnet + Copyright (C) 2017 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file set/plugin_block_set_test.c + * @brief set test block, recognizes elements with non-zero first byte as invalid + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_block_plugin.h" +#include "gnunet_block_group_lib.h" + + +/** + * Function called to validate a reply or a request. For + * request evaluation, simply pass "NULL" for the reply_block. + * + * @param cls closure + * @param ctx block context + * @param type block type + * @param group block group to use + * @param eo control flags + * @param query original query (hash) + * @param xquery extrended query data (can be NULL, depending on type) + * @param xquery_size number of bytes in xquery + * @param reply_block response to validate + * @param reply_block_size number of bytes in reply block + * @return characterization of result + */ +static enum GNUNET_BLOCK_EvaluationResult +block_plugin_set_test_evaluate (void *cls, + struct GNUNET_BLOCK_Context *ctx, + enum GNUNET_BLOCK_Type type, + struct GNUNET_BLOCK_Group *group, + enum GNUNET_BLOCK_EvaluationOptions eo, + const struct GNUNET_HashCode *query, + const void *xquery, + size_t xquery_size, + const void *reply_block, + size_t reply_block_size) +{ + if ((NULL == reply_block) || + (reply_block_size == 0) || + (0 != ((char *) reply_block)[0])) + return GNUNET_BLOCK_EVALUATION_RESULT_INVALID; + return GNUNET_BLOCK_EVALUATION_OK_MORE; +} + + +/** + * Function called to obtain the key for a block. + * + * @param cls closure + * @param type block type + * @param block block to get the key for + * @param block_size number of bytes in block + * @param key set to the key (query) for the given block + * @return #GNUNET_OK on success, #GNUNET_SYSERR if type not supported + * (or if extracting a key from a block of this type does not work) + */ +static int +block_plugin_set_test_get_key (void *cls, + enum GNUNET_BLOCK_Type type, + const void *block, + size_t block_size, + struct GNUNET_HashCode *key) +{ + return GNUNET_SYSERR; +} + + +/** + * Entry point for the plugin. + */ +void * +libgnunet_plugin_block_set_test_init (void *cls) +{ + static enum GNUNET_BLOCK_Type types[] = { + GNUNET_BLOCK_TYPE_SET_TEST, + GNUNET_BLOCK_TYPE_ANY /* end of list */ + }; + struct GNUNET_BLOCK_PluginFunctions *api; + + api = GNUNET_new (struct GNUNET_BLOCK_PluginFunctions); + api->evaluate = &block_plugin_set_test_evaluate; + api->get_key = &block_plugin_set_test_get_key; + api->types = types; + return api; +} + + +/** + * Exit point from the plugin. + */ +void * +libgnunet_plugin_block_set_test_done (void *cls) +{ + struct GNUNET_BLOCK_PluginFunctions *api = cls; + + GNUNET_free (api); + return NULL; +} + + +/* end of plugin_block_set_test.c */ diff --git a/src/seti/seti.conf.in b/src/seti/seti.conf.in new file mode 100644 index 000000000..e4f7b60b5 --- /dev/null +++ b/src/seti/seti.conf.in @@ -0,0 +1,12 @@ +[seti] +START_ON_DEMAND = @START_ON_DEMAND@ +@UNIXONLY@PORT = 2106 +HOSTNAME = localhost +BINARY = gnunet-service-seti +ACCEPT_FROM = 127.0.0.1; +ACCEPT_FROM6 = ::1; +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-seti.sock +UNIX_MATCH_UID = YES +UNIX_MATCH_GID = YES + +#PREFIX = valgrind diff --git a/src/seti/seti.h b/src/seti/seti.h new file mode 100644 index 000000000..aa7014034 --- /dev/null +++ b/src/seti/seti.h @@ -0,0 +1,267 @@ +/* + This file is part of GNUnet. + Copyright (C) 2012-2014, 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file set/seti.h + * @brief messages used for the set intersection api + * @author Florian Dold + * @author Christian Grothoff + */ +#ifndef SETI_H +#define SETI_H + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_set_service.h" + +GNUNET_NETWORK_STRUCT_BEGIN + +/** + * Message sent by the client to the service to ask starting + * a new set to perform operations with. + */ +struct GNUNET_SETI_CreateMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SETI_CREATE + */ + struct GNUNET_MessageHeader header; +}; + + +/** + * Message sent by the client to the service to start listening for + * incoming requests to perform a certain type of set operation for a + * certain type of application. + */ +struct GNUNET_SETI_ListenMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SETI_LISTEN + */ + struct GNUNET_MessageHeader header; + + /** + * Operation type, values of `enum GNUNET_SETI_OperationType` + */ + uint32_t operation GNUNET_PACKED; + + /** + * application id + */ + struct GNUNET_HashCode app_id; +}; + + +/** + * Message sent by a listening client to the service to accept + * performing the operation with the other peer. + */ +struct GNUNET_SETI_AcceptMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SETI_ACCEPT + */ + struct GNUNET_MessageHeader header; + + /** + * ID of the incoming request we want to accept. + */ + uint32_t accept_reject_id GNUNET_PACKED; + + /** + * Request ID to identify responses. + */ + uint32_t request_id GNUNET_PACKED; + + /** + * Return the intersection (1), instead of the elements to + * remove / the delta (0), in NBO. + */ + uint32_t return_intersection; + +}; + + +/** + * Message sent by a listening client to the service to reject + * performing the operation with the other peer. + */ +struct GNUNET_SETI_RejectMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SETI_REJECT + */ + struct GNUNET_MessageHeader header; + + /** + * ID of the incoming request we want to reject. + */ + uint32_t accept_reject_id GNUNET_PACKED; +}; + + +/** + * A request for an operation with another client. + */ +struct GNUNET_SETI_RequestMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SETI_REQUEST. + */ + struct GNUNET_MessageHeader header; + + /** + * ID of the to identify the request when accepting or + * rejecting it. + */ + uint32_t accept_id GNUNET_PACKED; + + /** + * Identity of the requesting peer. + */ + struct GNUNET_PeerIdentity peer_id; + + /* rest: context message, that is, application-specific + message to convince listener to pick up */ +}; + + +/** + * Message sent by client to service to initiate a set operation as a + * client (not as listener). A set (which determines the operation + * type) must already exist in association with this client. + */ +struct GNUNET_SETI_EvaluateMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SETI_EVALUATE + */ + struct GNUNET_MessageHeader header; + + /** + * Id of our set to evaluate, chosen implicitly by the client when it + * calls #GNUNET_SETI_commit(). + */ + uint32_t request_id GNUNET_PACKED; + + /** + * Peer to evaluate the operation with + */ + struct GNUNET_PeerIdentity target_peer; + + /** + * Application id + */ + struct GNUNET_HashCode app_id; + + /** + * Return the intersection (1), instead of the elements to + * remove / the delta (0), in NBO. + */ + uint32_t return_intersection; + + /* rest: context message, that is, application-specific + message to convince listener to pick up */ +}; + + +/** + * Message sent by the service to the client to indicate an + * element that is removed (set intersection) or added + * (set union) or part of the final result, depending on + * options specified for the operation. + */ +struct GNUNET_SETI_ResultMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SETI_RESULT + */ + struct GNUNET_MessageHeader header; + + /** + * Current set size. + */ + uint64_t current_size; + + /** + * id the result belongs to + */ + uint32_t request_id GNUNET_PACKED; + + /** + * Was the evaluation successful? Contains + * an `enum GNUNET_SETI_Status` in NBO. + */ + uint16_t result_status GNUNET_PACKED; + + /** + * Type of the element attachted to the message, if any. + */ + uint16_t element_type GNUNET_PACKED; + + /* rest: the actual element */ +}; + + +/** + * Message sent by client to the service to add an element to the set. + */ +struct GNUNET_SETI_ElementMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SETI_ADD. + */ + struct GNUNET_MessageHeader header; + + /** + * Type of the element to add or remove. + */ + uint16_t element_type GNUNET_PACKED; + + /** + * For alignment, always zero. + */ + uint16_t reserved GNUNET_PACKED; + + /* rest: the actual element */ +}; + + +/** + * Sent to the service by the client + * in order to cancel a set operation. + */ +struct GNUNET_SETI_CancelMessage +{ + /** + * Type: #GNUNET_MESSAGE_TYPE_SETI_CANCEL + */ + struct GNUNET_MessageHeader header; + + /** + * ID of the request we want to cancel. + */ + uint32_t request_id GNUNET_PACKED; +}; + + +GNUNET_NETWORK_STRUCT_END + +#endif diff --git a/src/seti/seti_api.c b/src/seti/seti_api.c new file mode 100644 index 000000000..d80a60684 --- /dev/null +++ b/src/seti/seti_api.c @@ -0,0 +1,895 @@ +/* + This file is part of GNUnet. + Copyright (C) 2012-2016, 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file seti/seti_api.c + * @brief api for the set service + * @author Florian Dold + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_seti_service.h" +#include "seti.h" + + +#define LOG(kind, ...) GNUNET_log_from (kind, "seti-api", __VA_ARGS__) + + +/** + * Opaque handle to a set. + */ +struct GNUNET_SETI_Handle +{ + /** + * Message queue for @e client. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Linked list of operations on the set. + */ + struct GNUNET_SETI_OperationHandle *ops_head; + + /** + * Linked list of operations on the set. + */ + struct GNUNET_SETI_OperationHandle *ops_tail; + + /** + * Configuration, needed when creating (lazy) copies. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Should the set be destroyed once all operations are gone? + * #GNUNET_SYSERR if #GNUNET_SETI_destroy() must raise this flag, + * #GNUNET_YES if #GNUNET_SETI_destroy() did raise this flag. + */ + int destroy_requested; + + /** + * Has the set become invalid (e.g. service died)? + */ + int invalid; + + /** + * Both client and service count the number of iterators + * created so far to match replies with iterators. + */ + uint16_t iteration_id; + +}; + + +/** + * Handle for a set operation request from another peer. + */ +struct GNUNET_SETI_Request +{ + /** + * Id of the request, used to identify the request when + * accepting/rejecting it. + */ + uint32_t accept_id; + + /** + * Has the request been accepted already? + * #GNUNET_YES/#GNUNET_NO + */ + int accepted; +}; + + +/** + * Handle to an operation. Only known to the service after committing + * the handle with a set. + */ +struct GNUNET_SETI_OperationHandle +{ + /** + * Function to be called when we have a result, + * or an error. + */ + GNUNET_SETI_ResultIterator result_cb; + + /** + * Closure for @e result_cb. + */ + void *result_cls; + + /** + * Local set used for the operation, + * NULL if no set has been provided by conclude yet. + */ + struct GNUNET_SETI_Handle *set; + + /** + * Message sent to the server on calling conclude, + * NULL if conclude has been called. + */ + struct GNUNET_MQ_Envelope *conclude_mqm; + + /** + * Address of the request if in the conclude message, + * used to patch the request id into the message when the set is known. + */ + uint32_t *request_id_addr; + + /** + * Handles are kept in a linked list. + */ + struct GNUNET_SETI_OperationHandle *prev; + + /** + * Handles are kept in a linked list. + */ + struct GNUNET_SETI_OperationHandle *next; + + /** + * Request ID to identify the operation within the set. + */ + uint32_t request_id; + + /** + * Should we return the resulting intersection (ADD) or + * the elements to remove (DEL)? + */ + int return_intersection; +}; + + +/** + * Opaque handle to a listen operation. + */ +struct GNUNET_SETI_ListenHandle +{ + /** + * Message queue for the client. + */ + struct GNUNET_MQ_Handle*mq; + + /** + * Configuration handle for the listener, stored + * here to be able to reconnect transparently on + * connection failure. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Function to call on a new incoming request, + * or on error. + */ + GNUNET_SETI_ListenCallback listen_cb; + + /** + * Closure for @e listen_cb. + */ + void *listen_cls; + + /** + * Task for reconnecting when the listener fails. + */ + struct GNUNET_SCHEDULER_Task *reconnect_task; + + /** + * Application ID we listen for. + */ + struct GNUNET_HashCode app_id; + + /** + * Time to wait until we try to reconnect on failure. + */ + struct GNUNET_TIME_Relative reconnect_backoff; + +}; + + +/** + * Check that the given @a msg is well-formed. + * + * @param cls closure + * @param msg message to check + * @return #GNUNET_OK if message is well-formed + */ +static int +check_result (void *cls, + const struct GNUNET_SETI_ResultMessage *msg) +{ + /* minimum size was already checked, everything else is OK! */ + return GNUNET_OK; +} + + +/** + * Handle result message for a set operation. + * + * @param cls the set + * @param mh the message + */ +static void +handle_result (void *cls, + const struct GNUNET_SETI_ResultMessage *msg) +{ + struct GNUNET_SETI_Handle *set = cls; + struct GNUNET_SETI_OperationHandle *oh; + struct GNUNET_SETI_Element e; + enum GNUNET_SETI_Status result_status; + int destroy_set; + + GNUNET_assert (NULL != set->mq); + result_status = (enum GNUNET_SETI_Status) ntohs (msg->result_status); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Got result message with status %d\n", + result_status); + oh = GNUNET_MQ_assoc_get (set->mq, + ntohl (msg->request_id)); + if (NULL == oh) + { + /* 'oh' can be NULL if we canceled the operation, but the service + did not get the cancel message yet. */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Ignoring result from canceled operation\n"); + return; + } + + switch (result_status) + { + case GNUNET_SETI_STATUS_ADD_LOCAL: + case GNUNET_SETI_STATUS_DEL_LOCAL: + e.data = &msg[1]; + e.size = ntohs (msg->header.size) + - sizeof(struct GNUNET_SETI_ResultMessage); + e.element_type = ntohs (msg->element_type); + if (NULL != oh->result_cb) + oh->result_cb (oh->result_cls, + &e, + GNUNET_ntohll (msg->current_size), + result_status); + return; + case GNUNET_SETI_STATUS_FAILURE: + case GNUNET_SETI_STATUS_DONE: + GNUNET_MQ_assoc_remove (set->mq, + ntohl (msg->request_id)); + GNUNET_CONTAINER_DLL_remove (set->ops_head, + set->ops_tail, + oh); + /* Need to do this calculation _before_ the result callback, + as IF the application still has a valid set handle, it + may trigger destruction of the set during the callback. */ + destroy_set = (GNUNET_YES == set->destroy_requested) && + (NULL == set->ops_head); + if (NULL != oh->result_cb) + { + oh->result_cb (oh->result_cls, + NULL, + GNUNET_ntohll (msg->current_size), + result_status); + } + else + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "No callback for final status\n"); + } + if (destroy_set) + GNUNET_SETI_destroy (set); + GNUNET_free (oh); + return; + } +} + + +/** + * Destroy the given set operation. + * + * @param oh set operation to destroy + */ +static void +set_operation_destroy (struct GNUNET_SETI_OperationHandle *oh) +{ + struct GNUNET_SETI_Handle *set = oh->set; + struct GNUNET_SETI_OperationHandle *h_assoc; + + if (NULL != oh->conclude_mqm) + GNUNET_MQ_discard (oh->conclude_mqm); + /* is the operation already commited? */ + if (NULL != set) + { + GNUNET_CONTAINER_DLL_remove (set->ops_head, + set->ops_tail, + oh); + h_assoc = GNUNET_MQ_assoc_remove (set->mq, + oh->request_id); + GNUNET_assert ((NULL == h_assoc) || + (h_assoc == oh)); + } + GNUNET_free (oh); +} + + +/** + * Cancel the given set operation. We need to send an explicit cancel + * message, as all operations one one set communicate using one + * handle. + * + * @param oh set operation to cancel + */ +void +GNUNET_SETI_operation_cancel (struct GNUNET_SETI_OperationHandle *oh) +{ + struct GNUNET_SETI_Handle *set = oh->set; + struct GNUNET_SETI_CancelMessage *m; + struct GNUNET_MQ_Envelope *mqm; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Cancelling SET operation\n"); + if (NULL != set) + { + mqm = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_SETI_CANCEL); + m->request_id = htonl (oh->request_id); + GNUNET_MQ_send (set->mq, mqm); + } + set_operation_destroy (oh); + if ((NULL != set) && + (GNUNET_YES == set->destroy_requested) && + (NULL == set->ops_head)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Destroying set after operation cancel\n"); + GNUNET_SETI_destroy (set); + } +} + + +/** + * We encountered an error communicating with the set service while + * performing a set operation. Report to the application. + * + * @param cls the `struct GNUNET_SETI_Handle` + * @param error error code + */ +static void +handle_client_set_error (void *cls, + enum GNUNET_MQ_Error error) +{ + struct GNUNET_SETI_Handle *set = cls; + + LOG (GNUNET_ERROR_TYPE_ERROR, + "Handling client set error %d\n", + error); + while (NULL != set->ops_head) + { + if ((NULL != set->ops_head->result_cb) && + (GNUNET_NO == set->destroy_requested)) + set->ops_head->result_cb (set->ops_head->result_cls, + NULL, + 0, + GNUNET_SETI_STATUS_FAILURE); + set_operation_destroy (set->ops_head); + } + set->invalid = GNUNET_YES; +} + + +/** + * Create an empty set. + * + * @param cfg configuration to use for connecting to the + * set service + * @return a handle to the set + */ +struct GNUNET_SETI_Handle * +GNUNET_SETI_create (const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_SETI_Handle *set = GNUNET_new (struct GNUNET_SETI_Handle); + struct GNUNET_MQ_MessageHandler mq_handlers[] = { + GNUNET_MQ_hd_var_size (result, + GNUNET_MESSAGE_TYPE_SETI_RESULT, + struct GNUNET_SETI_ResultMessage, + set), + GNUNET_MQ_handler_end () + }; + struct GNUNET_MQ_Envelope *mqm; + struct GNUNET_SETI_CreateMessage *create_msg; + + set->cfg = cfg; + set->mq = GNUNET_CLIENT_connect (cfg, + "set", + mq_handlers, + &handle_client_set_error, + set); + if (NULL == set->mq) + { + GNUNET_free (set); + return NULL; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Creating new intersection set\n"); + mqm = GNUNET_MQ_msg (create_msg, + GNUNET_MESSAGE_TYPE_SETI_CREATE); + GNUNET_MQ_send (set->mq, + mqm); + return set; +} + + +/** + * Add an element to the given set. After the element has been added + * (in the sense of being transmitted to the set service), @a cont + * will be called. Multiple calls to GNUNET_SETI_add_element() can be + * queued. + * + * @param set set to add element to + * @param element element to add to the set + * @param cb continuation called after the element has been added + * @param cb_cls closure for @a cont + * @return #GNUNET_OK on success, #GNUNET_SYSERR if the + * set is invalid (e.g. the set service crashed) + */ +int +GNUNET_SETI_add_element (struct GNUNET_SETI_Handle *set, + const struct GNUNET_SETI_Element *element, + GNUNET_SCHEDULER_TaskCallback cb, + void *cb_cls) +{ + struct GNUNET_MQ_Envelope *mqm; + struct GNUNET_SETI_ElementMessage *msg; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "adding element of type %u to set %p\n", + (unsigned int) element->element_type, + set); + if (GNUNET_YES == set->invalid) + { + if (NULL != cb) + cb (cb_cls); + return GNUNET_SYSERR; + } + mqm = GNUNET_MQ_msg_extra (msg, + element->size, + GNUNET_MESSAGE_TYPE_SETI_ADD); + msg->element_type = htons (element->element_type); + GNUNET_memcpy (&msg[1], + element->data, + element->size); + GNUNET_MQ_notify_sent (mqm, + cb, + cb_cls); + GNUNET_MQ_send (set->mq, + mqm); + return GNUNET_OK; +} + + +/** + * Destroy the set handle if no operations are left, mark the set + * for destruction otherwise. + * + * @param set set handle to destroy + */ +void +GNUNET_SETI_destroy (struct GNUNET_SETI_Handle *set) +{ + /* destroying set while iterator is active is currently + not supported; we should expand the API to allow + clients to explicitly cancel the iteration! */ + if ((NULL != set->ops_head) || + (GNUNET_SYSERR == set->destroy_requested)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Set operations are pending, delaying set destruction\n"); + set->destroy_requested = GNUNET_YES; + return; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Really destroying set\n"); + if (NULL != set->mq) + { + GNUNET_MQ_destroy (set->mq); + set->mq = NULL; + } + GNUNET_free (set); +} + + +/** + * Prepare a set operation to be evaluated with another peer. + * The evaluation will not start until the client provides + * a local set with #GNUNET_SETI_commit(). + * + * @param other_peer peer with the other set + * @param app_id hash for the application using the set + * @param context_msg additional information for the request + * @param options options to use when processing the request + * @param result_cb called on error or success + * @param result_cls closure for @e result_cb + * @return a handle to cancel the operation + */ +struct GNUNET_SETI_OperationHandle * +GNUNET_SETI_prepare (const struct GNUNET_PeerIdentity *other_peer, + const struct GNUNET_HashCode *app_id, + const struct GNUNET_MessageHeader *context_msg, + const struct GNUNET_SETI_Option options[], + GNUNET_SETI_ResultIterator result_cb, + void *result_cls) +{ + struct GNUNET_MQ_Envelope *mqm; + struct GNUNET_SETI_OperationHandle *oh; + struct GNUNET_SETI_EvaluateMessage *msg; + + oh = GNUNET_new (struct GNUNET_SETI_OperationHandle); + oh->result_cb = result_cb; + oh->result_cls = result_cls; + mqm = GNUNET_MQ_msg_nested_mh (msg, + GNUNET_MESSAGE_TYPE_SETI_EVALUATE, + context_msg); + msg->app_id = *app_id; + msg->target_peer = *other_peer; + for (const struct GNUNET_SETI_Option *opt = options; + GNUNET_SETI_OPTION_END != opt->type; + opt++) + { + switch (opt->type) + { + case GNUNET_SETI_OPTION_RETURN_INTERSECTION: + msg->return_intersection = GNUNET_YES; + break; + default: + LOG (GNUNET_ERROR_TYPE_ERROR, + "Option with type %d not recognized\n", + (int) opt->type); + } + } + oh->conclude_mqm = mqm; + oh->request_id_addr = &msg->request_id; + return oh; +} + + +/** + * Connect to the set service in order to listen for requests. + * + * @param cls the `struct GNUNET_SETI_ListenHandle *` to connect + */ +static void +listen_connect (void *cls); + + +/** + * Check validity of request message for a listen operation + * + * @param cls the listen handle + * @param msg the message + * @return #GNUNET_OK if the message is well-formed + */ +static int +check_request (void *cls, + const struct GNUNET_SETI_RequestMessage *msg) +{ + const struct GNUNET_MessageHeader *context_msg; + + if (ntohs (msg->header.size) == sizeof(*msg)) + return GNUNET_OK; /* no context message is OK */ + context_msg = GNUNET_MQ_extract_nested_mh (msg); + if (NULL == context_msg) + { + /* malformed context message is NOT ok */ + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Handle request message for a listen operation + * + * @param cls the listen handle + * @param msg the message + */ +static void +handle_request (void *cls, + const struct GNUNET_SETI_RequestMessage *msg) +{ + struct GNUNET_SETI_ListenHandle *lh = cls; + struct GNUNET_SETI_Request req; + const struct GNUNET_MessageHeader *context_msg; + struct GNUNET_MQ_Envelope *mqm; + struct GNUNET_SETI_RejectMessage *rmsg; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Processing incoming operation request with id %u\n", + ntohl (msg->accept_id)); + /* we got another valid request => reset the backoff */ + lh->reconnect_backoff = GNUNET_TIME_UNIT_MILLISECONDS; + req.accept_id = ntohl (msg->accept_id); + req.accepted = GNUNET_NO; + context_msg = GNUNET_MQ_extract_nested_mh (msg); + /* calling #GNUNET_SETI_accept() in the listen cb will set req->accepted */ + lh->listen_cb (lh->listen_cls, + &msg->peer_id, + context_msg, + &req); + if (GNUNET_YES == req.accepted) + return; /* the accept-case is handled in #GNUNET_SETI_accept() */ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Rejected request %u\n", + ntohl (msg->accept_id)); + mqm = GNUNET_MQ_msg (rmsg, + GNUNET_MESSAGE_TYPE_SETI_REJECT); + rmsg->accept_reject_id = msg->accept_id; + GNUNET_MQ_send (lh->mq, + mqm); +} + + +/** + * Our connection with the set service encountered an error, + * re-initialize with exponential back-off. + * + * @param cls the `struct GNUNET_SETI_ListenHandle *` + * @param error reason for the disconnect + */ +static void +handle_client_listener_error (void *cls, + enum GNUNET_MQ_Error error) +{ + struct GNUNET_SETI_ListenHandle *lh = cls; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Listener broke down (%d), re-connecting\n", + (int) error); + GNUNET_MQ_destroy (lh->mq); + lh->mq = NULL; + lh->reconnect_task = GNUNET_SCHEDULER_add_delayed (lh->reconnect_backoff, + &listen_connect, + lh); + lh->reconnect_backoff = GNUNET_TIME_STD_BACKOFF (lh->reconnect_backoff); +} + + +/** + * Connect to the set service in order to listen for requests. + * + * @param cls the `struct GNUNET_SETI_ListenHandle *` to connect + */ +static void +listen_connect (void *cls) +{ + struct GNUNET_SETI_ListenHandle *lh = cls; + struct GNUNET_MQ_MessageHandler mq_handlers[] = { + GNUNET_MQ_hd_var_size (request, + GNUNET_MESSAGE_TYPE_SETI_REQUEST, + struct GNUNET_SETI_RequestMessage, + lh), + GNUNET_MQ_handler_end () + }; + struct GNUNET_MQ_Envelope *mqm; + struct GNUNET_SETI_ListenMessage *msg; + + lh->reconnect_task = NULL; + GNUNET_assert (NULL == lh->mq); + lh->mq = GNUNET_CLIENT_connect (lh->cfg, + "set", + mq_handlers, + &handle_client_listener_error, + lh); + if (NULL == lh->mq) + return; + mqm = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SETI_LISTEN); + msg->app_id = lh->app_id; + GNUNET_MQ_send (lh->mq, + mqm); +} + + +/** + * Wait for set operation requests for the given application id + * + * @param cfg configuration to use for connecting to + * the set service, needs to be valid for the lifetime of the listen handle + * @param app_id id of the application that handles set operation requests + * @param listen_cb called for each incoming request matching the operation + * and application id + * @param listen_cls handle for @a listen_cb + * @return a handle that can be used to cancel the listen operation + */ +struct GNUNET_SETI_ListenHandle * +GNUNET_SETI_listen (const struct GNUNET_CONFIGURATION_Handle *cfg, + const struct GNUNET_HashCode *app_id, + GNUNET_SETI_ListenCallback listen_cb, + void *listen_cls) +{ + struct GNUNET_SETI_ListenHandle *lh; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Starting listener for app %s\n", + GNUNET_h2s (app_id)); + lh = GNUNET_new (struct GNUNET_SETI_ListenHandle); + lh->listen_cb = listen_cb; + lh->listen_cls = listen_cls; + lh->cfg = cfg; + lh->app_id = *app_id; + lh->reconnect_backoff = GNUNET_TIME_UNIT_MILLISECONDS; + listen_connect (lh); + if (NULL == lh->mq) + { + GNUNET_free (lh); + return NULL; + } + return lh; +} + + +/** + * Cancel the given listen operation. + * + * @param lh handle for the listen operation + */ +void +GNUNET_SETI_listen_cancel (struct GNUNET_SETI_ListenHandle *lh) +{ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Canceling listener %s\n", + GNUNET_h2s (&lh->app_id)); + if (NULL != lh->mq) + { + GNUNET_MQ_destroy (lh->mq); + lh->mq = NULL; + } + if (NULL != lh->reconnect_task) + { + GNUNET_SCHEDULER_cancel (lh->reconnect_task); + lh->reconnect_task = NULL; + } + GNUNET_free (lh); +} + + +/** + * Accept a request we got via #GNUNET_SETI_listen. Must be called during + * #GNUNET_SETI_listen, as the 'struct GNUNET_SETI_Request' becomes invalid + * afterwards. + * Call #GNUNET_SETI_commit to provide the local set to use for the operation, + * and to begin the exchange with the remote peer. + * + * @param request request to accept + * @param options options to use when processing the request + * @param result_cb callback for the results + * @param result_cls closure for @a result_cb + * @return a handle to cancel the operation + */ +struct GNUNET_SETI_OperationHandle * +GNUNET_SETI_accept (struct GNUNET_SETI_Request *request, + const struct GNUNET_SETI_Option options[], + GNUNET_SETI_ResultIterator result_cb, + void *result_cls) +{ + struct GNUNET_MQ_Envelope *mqm; + struct GNUNET_SETI_OperationHandle *oh; + struct GNUNET_SETI_AcceptMessage *msg; + + GNUNET_assert (GNUNET_NO == request->accepted); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Client accepts set intersection operation with id %u\n", + request->accept_id); + request->accepted = GNUNET_YES; + mqm = GNUNET_MQ_msg (msg, + GNUNET_MESSAGE_TYPE_SETI_ACCEPT); + msg->accept_reject_id = htonl (request->accept_id); + oh = GNUNET_new (struct GNUNET_SETI_OperationHandle); + oh->result_cb = result_cb; + oh->result_cls = result_cls; + oh->conclude_mqm = mqm; + oh->request_id_addr = &msg->request_id; + for (const struct GNUNET_SETI_Option *opt = options; + GNUNET_SETI_OPTION_END != opt->type; + opt++) + { + switch (opt->type) + { + case GNUNET_SETI_OPTION_RETURN_INTERSECTION: + oh->return_intersection = GNUNET_YES; + break; + default: + LOG (GNUNET_ERROR_TYPE_ERROR, + "Option with type %d not recognized\n", + (int) opt->type); + } + } + return oh; +} + + +/** + * Commit a set to be used with a set operation. + * This function is called once we have fully constructed + * the set that we want to use for the operation. At this + * time, the P2P protocol can then begin to exchange the + * set information and call the result callback with the + * result information. + * + * @param oh handle to the set operation + * @param set the set to use for the operation + * @return #GNUNET_OK on success, #GNUNET_SYSERR if the + * set is invalid (e.g. the set service crashed) + */ +int +GNUNET_SETI_commit (struct GNUNET_SETI_OperationHandle *oh, + struct GNUNET_SETI_Handle *set) +{ + if (NULL != oh->set) + { + /* Some other set was already committed for this + * operation, there is a logic bug in the client of this API */ + GNUNET_break (0); + return GNUNET_OK; + } + GNUNET_assert (NULL != set); + if (GNUNET_YES == set->invalid) + return GNUNET_SYSERR; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Client commits to SET\n"); + GNUNET_assert (NULL != oh->conclude_mqm); + oh->set = set; + GNUNET_CONTAINER_DLL_insert (set->ops_head, + set->ops_tail, + oh); + oh->request_id = GNUNET_MQ_assoc_add (set->mq, + oh); + *oh->request_id_addr = htonl (oh->request_id); + GNUNET_MQ_send (set->mq, + oh->conclude_mqm); + oh->conclude_mqm = NULL; + oh->request_id_addr = NULL; + return GNUNET_OK; +} + + +/** + * Hash a set element. + * + * @param element the element that should be hashed + * @param[out] ret_hash a pointer to where the hash of @a element + * should be stored + */ +void +GNUNET_SETI_element_hash (const struct GNUNET_SETI_Element *element, + struct GNUNET_HashCode *ret_hash) +{ + struct GNUNET_HashContext *ctx = GNUNET_CRYPTO_hash_context_start (); + + /* It's not guaranteed that the element data is always after the element header, + so we need to hash the chunks separately. */ + GNUNET_CRYPTO_hash_context_read (ctx, + &element->size, + sizeof(uint16_t)); + GNUNET_CRYPTO_hash_context_read (ctx, + &element->element_type, + sizeof(uint16_t)); + GNUNET_CRYPTO_hash_context_read (ctx, + element->data, + element->size); + GNUNET_CRYPTO_hash_context_finish (ctx, + ret_hash); +} + + +/* end of seti_api.c */ diff --git a/src/seti/test_seti.conf b/src/seti/test_seti.conf new file mode 100644 index 000000000..21fe984f8 --- /dev/null +++ b/src/seti/test_seti.conf @@ -0,0 +1,33 @@ +@INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf + +[PATHS] +GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-set/ + +[set] +START_ON_DEMAND = YES +#PREFIX = valgrind --leak-check=full +#PREFIX = gdbserver :1234 +OPTIONS = -L INFO + +[transport] +PLUGINS = unix +OPTIONS = -LERROR + +[nat] +RETURN_LOCAL_ADDRESSES = YES +DISABLEV6 = YES +USE_LOCALADDR = YES + +[peerinfo] +NO_IO = YES + +[nat] +# Use addresses from the local network interfaces (inluding loopback, but also others) +USE_LOCALADDR = YES + +# Disable IPv6 support +DISABLEV6 = NO + +# Do we use addresses from localhost address ranges? (::1, 127.0.0.0/8) +RETURN_LOCAL_ADDRESSES = YES + diff --git a/src/seti/test_seti_api.c b/src/seti/test_seti_api.c new file mode 100644 index 000000000..42dedb846 --- /dev/null +++ b/src/seti/test_seti_api.c @@ -0,0 +1,393 @@ +/* + This file is part of GNUnet. + Copyright (C) 2012-2014 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file set/test_set_intersection_result_full.c + * @brief testcase for full result mode of the intersection set operation + * @author Christian Fuchs + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testing_lib.h" +#include "gnunet_set_service.h" + + +static int ret; + +static struct GNUNET_PeerIdentity local_id; + +static struct GNUNET_HashCode app_id; + +static struct GNUNET_SET_Handle *set1; + +static struct GNUNET_SET_Handle *set2; + +static struct GNUNET_SET_ListenHandle *listen_handle; + +static const struct GNUNET_CONFIGURATION_Handle *config; + +static int iter_count; + +static struct GNUNET_SCHEDULER_Task *tt; + +static struct GNUNET_SET_OperationHandle *oh1; + +static struct GNUNET_SET_OperationHandle *oh2; + + +static void +result_cb_set1 (void *cls, + const struct GNUNET_SET_Element *element, + uint64_t current_size, + enum GNUNET_SET_Status status) +{ + static int count; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Processing result set 1 (%d)\n", + status); + switch (status) + { + case GNUNET_SET_STATUS_OK: + count++; + break; + + case GNUNET_SET_STATUS_FAILURE: + oh1 = NULL; + ret = 1; + break; + + case GNUNET_SET_STATUS_DONE: + oh1 = NULL; + GNUNET_assert (1 == count); + GNUNET_SET_destroy (set1); + set1 = NULL; + if (NULL == set2) + GNUNET_SCHEDULER_shutdown (); + break; + + default: + GNUNET_assert (0); + } +} + + +static void +result_cb_set2 (void *cls, + const struct GNUNET_SET_Element *element, + uint64_t current_size, + enum GNUNET_SET_Status status) +{ + static int count; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Processing result set 2 (%d)\n", + status); + switch (status) + { + case GNUNET_SET_STATUS_OK: + count++; + break; + + case GNUNET_SET_STATUS_FAILURE: + oh2 = NULL; + ret = 1; + break; + + case GNUNET_SET_STATUS_DONE: + oh2 = NULL; + GNUNET_assert (1 == count); + GNUNET_SET_destroy (set2); + set2 = NULL; + if (NULL == set1) + GNUNET_SCHEDULER_shutdown (); + break; + + default: + GNUNET_assert (0); + } +} + + +static void +listen_cb (void *cls, + const struct GNUNET_PeerIdentity *other_peer, + const struct GNUNET_MessageHeader *context_msg, + struct GNUNET_SET_Request *request) +{ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "starting intersection by accepting and committing\n"); + GNUNET_assert (NULL != context_msg); + GNUNET_assert (ntohs (context_msg->type) == GNUNET_MESSAGE_TYPE_DUMMY); + oh2 = GNUNET_SET_accept (request, + GNUNET_SET_RESULT_FULL, + (struct GNUNET_SET_Option[]) { 0 }, + &result_cb_set2, + NULL); + GNUNET_SET_commit (oh2, + set2); +} + + +/** + * Start the set operation. + * + * @param cls closure, unused + */ +static void +start (void *cls) +{ + struct GNUNET_MessageHeader context_msg; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "starting listener\n"); + context_msg.size = htons (sizeof context_msg); + context_msg.type = htons (GNUNET_MESSAGE_TYPE_DUMMY); + listen_handle = GNUNET_SET_listen (config, + GNUNET_SET_OPERATION_INTERSECTION, + &app_id, + &listen_cb, + NULL); + oh1 = GNUNET_SET_prepare (&local_id, + &app_id, + &context_msg, + GNUNET_SET_RESULT_FULL, + (struct GNUNET_SET_Option[]) { 0 }, + &result_cb_set1, + NULL); + GNUNET_SET_commit (oh1, + set1); +} + + +/** + * Initialize the second set, continue + * + * @param cls closure, unused + */ +static void +init_set2 (void *cls) +{ + struct GNUNET_SET_Element element; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "initializing set 2\n"); + element.element_type = 0; + element.data = "hello"; + element.size = strlen (element.data); + GNUNET_SET_add_element (set2, + &element, + NULL, + NULL); + element.data = "quux"; + element.size = strlen (element.data); + GNUNET_SET_add_element (set2, + &element, + NULL, + NULL); + element.data = "baz"; + element.size = strlen (element.data); + GNUNET_SET_add_element (set2, + &element, + &start, + NULL); +} + + +/** + * Initialize the first set, continue. + */ +static void +init_set1 (void) +{ + struct GNUNET_SET_Element element; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "initializing set 1\n"); + element.element_type = 0; + element.data = "hello"; + element.size = strlen (element.data); + GNUNET_SET_add_element (set1, + &element, + NULL, + NULL); + element.data = "bar"; + element.size = strlen (element.data); + GNUNET_SET_add_element (set1, + &element, + &init_set2, + NULL); +} + + +static int +iter_cb (void *cls, + const struct GNUNET_SET_Element *element) +{ + if (NULL == element) + { + GNUNET_assert (iter_count == 3); + GNUNET_SET_destroy (cls); + return GNUNET_YES; + } + iter_count++; + return GNUNET_YES; +} + + +static void +test_iter () +{ + struct GNUNET_SET_Element element; + struct GNUNET_SET_Handle *iter_set; + + iter_set = GNUNET_SET_create (config, + GNUNET_SET_OPERATION_INTERSECTION); + element.element_type = 0; + element.data = "hello"; + element.size = strlen (element.data); + GNUNET_SET_add_element (iter_set, + &element, + NULL, + NULL); + element.data = "bar"; + element.size = strlen (element.data); + GNUNET_SET_add_element (iter_set, + &element, + NULL, + NULL); + element.data = "quux"; + element.size = strlen (element.data); + GNUNET_SET_add_element (iter_set, + &element, + NULL, + NULL); + GNUNET_SET_iterate (iter_set, + &iter_cb, + iter_set); +} + + +/** + * Function run on shutdown. + * + * @param cls closure + */ +static void +do_shutdown (void *cls) +{ + if (NULL != tt) + { + GNUNET_SCHEDULER_cancel (tt); + tt = NULL; + } + if (NULL != oh1) + { + GNUNET_SET_operation_cancel (oh1); + oh1 = NULL; + } + if (NULL != oh2) + { + GNUNET_SET_operation_cancel (oh2); + oh2 = NULL; + } + if (NULL != set1) + { + GNUNET_SET_destroy (set1); + set1 = NULL; + } + if (NULL != set2) + { + GNUNET_SET_destroy (set2); + set2 = NULL; + } + if (NULL != listen_handle) + { + GNUNET_SET_listen_cancel (listen_handle); + listen_handle = NULL; + } +} + + +/** + * Function run on timeout. + * + * @param cls closure + */ +static void +timeout_fail (void *cls) +{ + tt = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, + "Testcase failed with timeout\n"); + GNUNET_SCHEDULER_shutdown (); + ret = 1; +} + + +/** + * Signature of the 'main' function for a (single-peer) testcase that + * is run using 'GNUNET_TESTING_peer_run'. + * + * @param cls closure + * @param cfg configuration of the peer that was started + * @param peer identity of the peer that was created + */ +static void +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Peer *peer) +{ + config = cfg; + GNUNET_TESTING_peer_get_identity (peer, + &local_id); + if (0) + test_iter (); + + tt = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply ( + GNUNET_TIME_UNIT_SECONDS, 5), + &timeout_fail, + NULL); + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, + NULL); + + set1 = GNUNET_SET_create (cfg, + GNUNET_SET_OPERATION_INTERSECTION); + set2 = GNUNET_SET_create (cfg, + GNUNET_SET_OPERATION_INTERSECTION); + GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, + &app_id); + + /* test the real set reconciliation */ + init_set1 (); +} + + +int +main (int argc, + char **argv) +{ + if (0 != GNUNET_TESTING_peer_run ("test_set_intersection_result_full", + "test_set.conf", + &run, NULL)) + return 1; + return ret; +} -- cgit v1.2.3 From 3fb3bf908f4977aef6bde6a450954e2704b14bcb Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 18 Aug 2020 19:31:39 +0200 Subject: -splitting of set intersection functionality from set service (not yet finished, FTBFS) --- src/seti/gnunet-service-seti.c | 997 ++++++++++------------------------------- 1 file changed, 244 insertions(+), 753 deletions(-) diff --git a/src/seti/gnunet-service-seti.c b/src/seti/gnunet-service-seti.c index 3b8da01cd..037181bde 100644 --- a/src/seti/gnunet-service-seti.c +++ b/src/seti/gnunet-service-seti.c @@ -107,28 +107,6 @@ struct ElementEntry; struct Operation; -/** - * MutationEvent gives information about changes - * to an element (removal / addition) in a set content. - */ -struct MutationEvent -{ - /** - * First generation affected by this mutation event. - * - * If @a generation is 0, this mutation event is a list - * sentinel element. - */ - unsigned int generation; - - /** - * If @a added is #GNUNET_YES, then this is a - * `remove` event, otherwise it is an `add` event. - */ - int added; -}; - - /** * Information about an element element in the set. All elements are * stored in a hash-table from their hash-code to their `struct @@ -150,20 +128,9 @@ struct ElementEntry struct GNUNET_HashCode element_hash; /** - * If @a mutations is not NULL, it contains - * a list of mutations, ordered by increasing generation. - * The list is terminated by a sentinel event with `generation` - * set to 0. - * - * If @a mutations is NULL, then this element exists in all generations - * of the respective set content this element belongs to. - */ - struct MutationEvent *mutations; - - /** - * Number of elements in the array @a mutations. + * Generation in which the element was added. */ - unsigned int mutations_size; + unsigned int generation_added; /** * #GNUNET_YES if the element is a remote element, and does not belong @@ -285,7 +252,13 @@ struct Operation /** * When are elements sent to the client, and which elements are sent? */ - enum GNUNET_SET_ResultMode result_mode; + int return_intersection; + + /** + * Lower bound for the set size, used only when + * byzantine mode is enabled. + */ + int byzantine_lower_bound; /** * Always use delta operation instead of sending full sets, @@ -305,12 +278,6 @@ struct Operation */ int byzantine; - /** - * Lower bound for the set size, used only when - * byzantine mode is enabled. - */ - int byzantine_lower_bound; - /** * Unique request id for the request from a remote peer, sent to the * client, which will accept or reject the request. Set to '0' iff @@ -319,8 +286,7 @@ struct Operation uint32_t suggest_id; /** - * Generation in which the operation handle - * was created. + * Generation in which the operation handle was created. */ unsigned int generation_created; }; @@ -337,20 +303,6 @@ struct SetContent */ struct GNUNET_CONTAINER_MultiHashMap *elements; - /** - * Mutations requested by the client that we're - * unable to execute right now because we're iterating - * over the underlying hash map of elements. - */ - struct PendingMutation *pending_mutations_head; - - /** - * Mutations requested by the client that we're - * unable to execute right now because we're iterating - * over the underlying hash map of elements. - */ - struct PendingMutation *pending_mutations_tail; - /** * Number of references to the content. */ @@ -368,49 +320,6 @@ struct SetContent }; -struct GenerationRange -{ - /** - * First generation that is excluded. - */ - unsigned int start; - - /** - * Generation after the last excluded generation. - */ - unsigned int end; -}; - - -/** - * Information about a mutation to apply to a set. - */ -struct PendingMutation -{ - /** - * Mutations are kept in a DLL. - */ - struct PendingMutation *prev; - - /** - * Mutations are kept in a DLL. - */ - struct PendingMutation *next; - - /** - * Set this mutation is about. - */ - struct Set *set; - - /** - * Message that describes the desired mutation. - * May only be a #GNUNET_MESSAGE_TYPE_SET_ADD or - * #GNUNET_MESSAGE_TYPE_SET_REMOVE. - */ - struct GNUNET_SET_ElementMessage *msg; -}; - - /** * A set that supports a specific operation with other peers. */ @@ -443,12 +352,6 @@ struct Set */ struct SetState *state; - /** - * Current state of iterating elements for the client. - * NULL if we are not currently iterating. - */ - struct GNUNET_CONTAINER_MultiHashMapIterator *iter; - /** * Evaluate operations are held in a linked list. */ @@ -459,37 +362,12 @@ struct Set */ struct Operation *ops_tail; - /** - * List of generations we have to exclude, due to lazy copies. - */ - struct GenerationRange *excluded_generations; - /** * Current generation, that is, number of previously executed * operations and lazy copies on the underlying set content. */ unsigned int current_generation; - /** - * Number of elements in array @a excluded_generations. - */ - unsigned int excluded_generations_size; - - /** - * Type of operation supported for this set - */ - enum GNUNET_SET_OperationType operation; - - /** - * Generation we're currently iteration over. - */ - unsigned int iter_generation; - - /** - * Each @e iter is assigned a unique number, so that the client - * can distinguish iterations. - */ - uint16_t iteration_id; }; @@ -724,7 +602,7 @@ send_client_removed_element (struct Operation *op, struct GNUNET_MQ_Envelope *ev; struct GNUNET_SET_ResultMessage *rm; - if (GNUNET_SET_RESULT_REMOVED != op->result_mode) + if (GNUNET_NO != op->return_intersection) return; /* Wrong mode for transmitting removed elements */ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending removed element (size %u) to client\n", @@ -736,13 +614,13 @@ send_client_removed_element (struct Operation *op, GNUNET_assert (0 != op->client_request_id); ev = GNUNET_MQ_msg_extra (rm, element->size, - GNUNET_MESSAGE_TYPE_SET_RESULT); + GNUNET_MESSAGE_TYPE_SETI_RESULT); if (NULL == ev) { GNUNET_break (0); return; } - rm->result_status = htons (GNUNET_SET_STATUS_OK); + rm->result_status = htons (GNUNET_SET_STATUS_DEL_LOCAL); rm->request_id = htonl (op->client_request_id); rm->element_type = element->element_type; GNUNET_memcpy (&rm[1], @@ -770,7 +648,6 @@ filtered_map_initialization (void *cls, struct ElementEntry *ee = value; struct GNUNET_HashCode mutated_hash; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "FIMA called for %s:%u\n", GNUNET_h2s (&ee->element_hash), @@ -934,8 +811,8 @@ fail_intersection_operation (struct Operation *op) op->state->my_elements = NULL; } ev = GNUNET_MQ_msg (msg, - GNUNET_MESSAGE_TYPE_SET_RESULT); - msg->result_status = htons (GNUNET_SET_STATUS_FAILURE); + GNUNET_MESSAGE_TYPE_SETI_RESULT); + msg->result_status = htons (GNUNET_SETI_STATUS_FAILURE); msg->request_id = htonl (op->client_request_id); msg->element_type = htons (0); GNUNET_MQ_send (op->set->cs->mq, @@ -999,7 +876,7 @@ send_bloomfilter (struct Operation *op) chunk_size = bf_size; ev = GNUNET_MQ_msg_extra (msg, chunk_size, - GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_BF); + GNUNET_MESSAGE_TYPE_SETI_INTERSECTION_P2P_BF); GNUNET_assert (GNUNET_SYSERR != GNUNET_CONTAINER_bloomfilter_get_raw_data ( op->state->local_bf, @@ -1028,7 +905,7 @@ send_bloomfilter (struct Operation *op) chunk_size = bf_size - offset; ev = GNUNET_MQ_msg_extra (msg, chunk_size, - GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_BF); + GNUNET_MESSAGE_TYPE_SETI_INTERSECTION_P2P_BF); GNUNET_memcpy (&msg[1], &bf_data[offset], chunk_size); @@ -1067,7 +944,7 @@ send_client_done_and_destroy (void *cls) 1, GNUNET_NO); ev = GNUNET_MQ_msg (rm, - GNUNET_MESSAGE_TYPE_SET_RESULT); + GNUNET_MESSAGE_TYPE_SETI_RESULT); rm->request_id = htonl (op->client_request_id); rm->result_status = htons (GNUNET_SET_STATUS_DONE); rm->element_type = htons (0); @@ -1114,7 +991,7 @@ send_p2p_done (struct Operation *op) GNUNET_assert (PHASE_MUST_SEND_DONE == op->state->phase); GNUNET_assert (GNUNET_NO == op->state->channel_death_expected); ev = GNUNET_MQ_msg (idm, - GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_DONE); + GNUNET_MESSAGE_TYPE_SETI_INTERSECTION_P2P_DONE); idm->final_element_count = htonl (op->state->my_element_count); idm->element_xor_hash = op->state->my_xor; GNUNET_MQ_notify_sent (ev, @@ -1176,7 +1053,7 @@ send_remaining_elements (void *cls) GNUNET_assert (0 != op->client_request_id); ev = GNUNET_MQ_msg_extra (rm, element->size, - GNUNET_MESSAGE_TYPE_SET_RESULT); + GNUNET_MESSAGE_TYPE_SETI_RESULT); GNUNET_assert (NULL != ev); rm->result_status = htons (GNUNET_SET_STATUS_OK); rm->request_id = htonl (op->client_request_id); @@ -1243,7 +1120,7 @@ send_element_count (struct Operation *op) "Sending our element count (%u)\n", op->state->my_element_count); ev = GNUNET_MQ_msg (msg, - GNUNET_MESSAGE_TYPE_SET_INTERSECTION_P2P_ELEMENT_INFO); + GNUNET_MESSAGE_TYPE_SETI_INTERSECTION_P2P_ELEMENT_INFO); msg->sender_element_count = htonl (op->state->my_element_count); GNUNET_MQ_send (op->mq, ev); } @@ -1273,7 +1150,7 @@ begin_bf_exchange (struct Operation *op) * @param cls the intersection operation * @param mh the header of the message */ -void +static void handle_intersection_p2p_element_info (void *cls, const struct IntersectionElementInfoMessage *msg) @@ -1327,7 +1204,6 @@ process_bf (struct Operation *op) GNUNET_break_op (0); fail_intersection_operation (op); return; - case PHASE_COUNT_SENT: /* This is the first BF being sent, build our initial map with filtering in place */ @@ -1336,24 +1212,20 @@ process_bf (struct Operation *op) &filtered_map_initialization, op); break; - case PHASE_BF_EXCHANGE: /* Update our set by reduction */ GNUNET_CONTAINER_multihashmap_iterate (op->state->my_elements, &iterator_bf_reduce, op); break; - case PHASE_MUST_SEND_DONE: GNUNET_break_op (0); fail_intersection_operation (op); return; - case PHASE_DONE_RECEIVED: GNUNET_break_op (0); fail_intersection_operation (op); return; - case PHASE_FINISHED: GNUNET_break_op (0); fail_intersection_operation (op); @@ -1420,7 +1292,7 @@ check_intersection_p2p_bf (void *cls, * @param cls the intersection operation * @param msg the header of the message */ -static +static void handle_intersection_p2p_bf (void *cls, const struct BFMessage *msg) { @@ -1612,214 +1484,6 @@ handle_intersection_p2p_done (void *cls, } -/** - * Initiate a set intersection operation with a remote peer. - * - * @param op operation that is created, should be initialized to - * begin the evaluation - * @param opaque_context message to be transmitted to the listener - * to convince it to accept, may be NULL - * @return operation-specific state to keep in @a op - */ -static struct OperationState * -intersection_evaluate (struct Operation *op, - const struct GNUNET_MessageHeader *opaque_context) -{ - struct OperationState *state; - struct GNUNET_MQ_Envelope *ev; - struct OperationRequestMessage *msg; - - ev = GNUNET_MQ_msg_nested_mh (msg, - GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST, - opaque_context); - if (NULL == ev) - { - /* the context message is too large!? */ - GNUNET_break (0); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Initiating intersection operation evaluation\n"); - state = GNUNET_new (struct OperationState); - /* we started the operation, thus we have to send the operation request */ - state->phase = PHASE_INITIAL; - state->my_element_count = op->set->state->current_set_element_count; - state->my_elements - = GNUNET_CONTAINER_multihashmap_create (state->my_element_count, - GNUNET_YES); - - msg->operation = htonl (GNUNET_SET_OPERATION_INTERSECTION); - msg->element_count = htonl (state->my_element_count); - GNUNET_MQ_send (op->mq, - ev); - state->phase = PHASE_COUNT_SENT; - if (NULL != opaque_context) - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sent op request with context message\n"); - else - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sent op request without context message\n"); - return state; -} - - -/** - * Accept an intersection operation request from a remote peer. Only - * initializes the private operation state. - * - * @param op operation that will be accepted as an intersection operation - */ -static struct OperationState * -intersection_accept (struct Operation *op) -{ - struct OperationState *state; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Accepting set intersection operation\n"); - state = GNUNET_new (struct OperationState); - state->phase = PHASE_INITIAL; - state->my_element_count - = op->set->state->current_set_element_count; - state->my_elements - = GNUNET_CONTAINER_multihashmap_create (GNUNET_MIN (state->my_element_count, - op->remote_element_count), - GNUNET_YES); - op->state = state; - if (op->remote_element_count < state->my_element_count) - { - /* If the other peer (Alice) has fewer elements than us (Bob), - we just send the count as Alice should send the first BF */ - send_element_count (op); - state->phase = PHASE_COUNT_SENT; - return state; - } - /* We have fewer elements, so we start with the BF */ - begin_bf_exchange (op); - return state; -} - - -/** - * Destroy the intersection operation. Only things specific to the - * intersection operation are destroyed. - * - * @param op intersection operation to destroy - */ -static void -intersection_op_cancel (struct Operation *op) -{ - /* check if the op was canceled twice */ - GNUNET_assert (NULL != op->state); - if (NULL != op->state->remote_bf) - { - GNUNET_CONTAINER_bloomfilter_free (op->state->remote_bf); - op->state->remote_bf = NULL; - } - if (NULL != op->state->local_bf) - { - GNUNET_CONTAINER_bloomfilter_free (op->state->local_bf); - op->state->local_bf = NULL; - } - if (NULL != op->state->my_elements) - { - GNUNET_CONTAINER_multihashmap_destroy (op->state->my_elements); - op->state->my_elements = NULL; - } - if (NULL != op->state->full_result_iter) - { - GNUNET_CONTAINER_multihashmap_iterator_destroy ( - op->state->full_result_iter); - op->state->full_result_iter = NULL; - } - GNUNET_free (op->state); - op->state = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Destroying intersection op state done\n"); -} - - -/** - * Create a new set supporting the intersection operation. - * - * @return the newly created set - */ -static struct SetState * -intersection_set_create () -{ - struct SetState *set_state; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Intersection set created\n"); - set_state = GNUNET_new (struct SetState); - set_state->current_set_element_count = 0; - - return set_state; -} - - -/** - * Add the element from the given element message to the set. - * - * @param set_state state of the set want to add to - * @param ee the element to add to the set - */ -static void -intersection_add (struct SetState *set_state, - struct ElementEntry *ee) -{ - set_state->current_set_element_count++; -} - - -/** - * Destroy a set that supports the intersection operation - * - * @param set_state the set to destroy - */ -static void -intersection_set_destroy (struct SetState *set_state) -{ - GNUNET_free (set_state); -} - - -/** - * Remove the element given in the element message from the set. - * - * @param set_state state of the set to remove from - * @param element set element to remove - */ -static void -intersection_remove (struct SetState *set_state, - struct ElementEntry *element) -{ - GNUNET_assert (0 < set_state->current_set_element_count); - set_state->current_set_element_count--; -} - - -/** - * Callback for channel death for the intersection operation. - * - * @param op operation that lost the channel - */ -static void -intersection_channel_death (struct Operation *op) -{ - if (GNUNET_YES == op->state->channel_death_expected) - { - /* oh goodie, we are done! */ - send_client_done_and_destroy (op); - } - else - { - /* sorry, channel went down early, too bad. */ - _GSS_operation_destroy (op, - GNUNET_YES); - } -} - - /** * Get the incoming socket associated with the given id. * @@ -1869,171 +1533,6 @@ incoming_destroy (struct Operation *op) } -/** - * Context for the #garbage_collect_cb(). - */ -struct GarbageContext -{ - /** - * Map for which we are garbage collecting removed elements. - */ - struct GNUNET_CONTAINER_MultiHashMap *map; - - /** - * Lowest generation for which an operation is still pending. - */ - unsigned int min_op_generation; - - /** - * Largest generation for which an operation is still pending. - */ - unsigned int max_op_generation; -}; - - -/** - * Function invoked to check if an element can be removed from - * the set's history because it is no longer needed. - * - * @param cls the `struct GarbageContext *` - * @param key key of the element in the map - * @param value the `struct ElementEntry *` - * @return #GNUNET_OK (continue to iterate) - */ -static int -garbage_collect_cb (void *cls, const struct GNUNET_HashCode *key, void *value) -{ - // struct GarbageContext *gc = cls; - // struct ElementEntry *ee = value; - - // if (GNUNET_YES != ee->removed) - // return GNUNET_OK; - // if ( (gc->max_op_generation < ee->generation_added) || - // (ee->generation_removed > gc->min_op_generation) ) - // { - // GNUNET_assert (GNUNET_YES == - // GNUNET_CONTAINER_multihashmap_remove (gc->map, - // key, - // ee)); - // GNUNET_free (ee); - // } - return GNUNET_OK; -} - - -/** - * Collect and destroy elements that are not needed anymore, because - * their lifetime (as determined by their generation) does not overlap - * with any active set operation. - * - * @param set set to garbage collect - */ -static void -collect_generation_garbage (struct Set *set) -{ - struct GarbageContext gc; - - gc.min_op_generation = UINT_MAX; - gc.max_op_generation = 0; - for (struct Operation *op = set->ops_head; NULL != op; op = op->next) - { - gc.min_op_generation = - GNUNET_MIN (gc.min_op_generation, op->generation_created); - gc.max_op_generation = - GNUNET_MAX (gc.max_op_generation, op->generation_created); - } - gc.map = set->content->elements; - GNUNET_CONTAINER_multihashmap_iterate (set->content->elements, - &garbage_collect_cb, - &gc); -} - - -/** - * Is @a generation in the range of exclusions? - * - * @param generation generation to query - * @param excluded array of generations where the element is excluded - * @param excluded_size length of the @a excluded array - * @return #GNUNET_YES if @a generation is in any of the ranges - */ -static int -is_excluded_generation (unsigned int generation, - struct GenerationRange *excluded, - unsigned int excluded_size) -{ - for (unsigned int i = 0; i < excluded_size; i++) - if ((generation >= excluded[i].start) && (generation < excluded[i].end)) - return GNUNET_YES; - return GNUNET_NO; -} - - -/** - * Is element @a ee part of the set during @a query_generation? - * - * @param ee element to test - * @param query_generation generation to query - * @param excluded array of generations where the element is excluded - * @param excluded_size length of the @a excluded array - * @return #GNUNET_YES if the element is in the set, #GNUNET_NO if not - */ -static int -is_element_of_generation (struct ElementEntry *ee, - unsigned int query_generation, - struct GenerationRange *excluded, - unsigned int excluded_size) -{ - struct MutationEvent *mut; - int is_present; - - GNUNET_assert (NULL != ee->mutations); - if (GNUNET_YES == - is_excluded_generation (query_generation, excluded, excluded_size)) - { - GNUNET_break (0); - return GNUNET_NO; - } - - is_present = GNUNET_NO; - - /* Could be made faster with binary search, but lists - are small, so why bother. */ - for (unsigned int i = 0; i < ee->mutations_size; i++) - { - mut = &ee->mutations[i]; - - if (mut->generation > query_generation) - { - /* The mutation doesn't apply to our generation - anymore. We can'b break here, since mutations aren't - sorted by generation. */ - continue; - } - - if (GNUNET_YES == - is_excluded_generation (mut->generation, excluded, excluded_size)) - { - /* The generation is excluded (because it belongs to another - fork via a lazy copy) and thus mutations aren't considered - for membership testing. */ - continue; - } - - /* This would be an inconsistency in how we manage mutations. */ - if ((GNUNET_YES == is_present) && (GNUNET_YES == mut->added)) - GNUNET_assert (0); - /* Likewise. */ - if ((GNUNET_NO == is_present) && (GNUNET_NO == mut->added)) - GNUNET_assert (0); - - is_present = mut->added; - } - - return is_present; -} - - /** * Is element @a ee part of the set used by @a op? * @@ -2041,13 +1540,11 @@ is_element_of_generation (struct ElementEntry *ee, * @param op operation the defines the set and its generation * @return #GNUNET_YES if the element is in the set, #GNUNET_NO if not */ -int -_GSS_is_element_of_operation (struct ElementEntry *ee, struct Operation *op) +static int +_GSS_is_element_of_operation (struct ElementEntry *ee, + struct Operation *op) { - return is_element_of_generation (ee, - op->generation_created, - op->set->excluded_generations, - op->set->excluded_generations_size); + return op->generation_created >= ee->generation_added; } @@ -2062,10 +1559,9 @@ _GSS_is_element_of_operation (struct ElementEntry *ee, struct Operation *op) * as there may be multiple operations per set. * * @param op operation to destroy - * @param gc #GNUNET_YES to perform garbage collection on the set */ -void -_GSS_operation_destroy (struct Operation *op, int gc) +static void +_GSS_operation_destroy (struct Operation *op) { struct Set *set = op->set; struct GNUNET_CADET_Channel *channel; @@ -2074,12 +1570,39 @@ _GSS_operation_destroy (struct Operation *op, int gc) GNUNET_assert (NULL == op->listener); if (NULL != op->state) { - intersection_cancel (op); // FIXME: inline + /* check if the op was canceled twice */ + GNUNET_assert (NULL != op->state); + if (NULL != op->state->remote_bf) + { + GNUNET_CONTAINER_bloomfilter_free (op->state->remote_bf); + op->state->remote_bf = NULL; + } + if (NULL != op->state->local_bf) + { + GNUNET_CONTAINER_bloomfilter_free (op->state->local_bf); + op->state->local_bf = NULL; + } + if (NULL != op->state->my_elements) + { + GNUNET_CONTAINER_multihashmap_destroy (op->state->my_elements); + op->state->my_elements = NULL; + } + if (NULL != op->state->full_result_iter) + { + GNUNET_CONTAINER_multihashmap_iterator_destroy ( + op->state->full_result_iter); + op->state->full_result_iter = NULL; + } + GNUNET_free (op->state); op->state = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Destroying intersection op state done\n"); } if (NULL != set) { - GNUNET_CONTAINER_DLL_remove (set->ops_head, set->ops_tail, op); + GNUNET_CONTAINER_DLL_remove (set->ops_head, + set->ops_tail, + op); op->set = NULL; } if (NULL != op->context_msg) @@ -2094,8 +1617,6 @@ _GSS_operation_destroy (struct Operation *op, int gc) op->channel = NULL; GNUNET_CADET_channel_destroy (channel); } - if ((NULL != set) && (GNUNET_YES == gc)) - collect_generation_garbage (set); /* We rely on the channel end handler to free 'op'. When 'op->channel' was NULL, * there was a channel end handler that will free 'op' on the call stack. */ } @@ -2166,8 +1687,6 @@ client_disconnect_cb (void *cls, if (NULL != (set = cs->set)) { struct SetContent *content = set->content; - struct PendingMutation *pm; - struct PendingMutation *pm_current; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Destroying client's set\n"); /* Destroy pending set operations */ @@ -2176,8 +1695,7 @@ client_disconnect_cb (void *cls, /* Destroy operation-specific state */ GNUNET_assert (NULL != set->state); - intersection_set_destroy (set->state); // FIXME: inline - set->state = NULL; + GNUNET_free (set->state); /* Clean up ongoing iterations */ if (NULL != set->iter) @@ -2187,21 +1705,6 @@ client_disconnect_cb (void *cls, set->iteration_id++; } - /* discard any pending mutations that reference this set */ - pm = content->pending_mutations_head; - while (NULL != pm) - { - pm_current = pm; - pm = pm->next; - if (pm_current->set == set) - { - GNUNET_CONTAINER_DLL_remove (content->pending_mutations_head, - content->pending_mutations_tail, - pm_current); - GNUNET_free (pm_current); - } - } - /* free set content (or at least decrement RC) */ set->content = NULL; GNUNET_assert (0 != content->refcount); @@ -2260,7 +1763,8 @@ client_disconnect_cb (void *cls, * #GNUNET_SYSERR to destroy the channel */ static int -check_incoming_msg (void *cls, const struct OperationRequestMessage *msg) +check_incoming_msg (void *cls, + const struct OperationRequestMessage *msg) { struct Operation *op = cls; struct Listener *listener = op->listener; @@ -2313,7 +1817,8 @@ check_incoming_msg (void *cls, const struct OperationRequestMessage *msg) * #GNUNET_SYSERR to destroy the channel */ static void -handle_incoming_msg (void *cls, const struct OperationRequestMessage *msg) +handle_incoming_msg (void *cls, + const struct OperationRequestMessage *msg) { struct Operation *op = cls; struct Listener *listener = op->listener; @@ -2357,121 +1862,6 @@ handle_incoming_msg (void *cls, const struct OperationRequestMessage *msg) } -/** - * Add an element to @a set as specified by @a msg - * - * @param set set to manipulate - * @param msg message specifying the change - */ -static void -execute_add (struct Set *set, const struct GNUNET_SET_ElementMessage *msg) -{ - struct GNUNET_SET_Element el; - struct ElementEntry *ee; - struct GNUNET_HashCode hash; - - GNUNET_assert (GNUNET_MESSAGE_TYPE_SETI_ADD == ntohs (msg->header.type)); - el.size = ntohs (msg->header.size) - sizeof(*msg); - el.data = &msg[1]; - el.element_type = ntohs (msg->element_type); - GNUNET_SET_element_hash (&el, &hash); - ee = GNUNET_CONTAINER_multihashmap_get (set->content->elements, &hash); - if (NULL == ee) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Client inserts element %s of size %u\n", - GNUNET_h2s (&hash), - el.size); - ee = GNUNET_malloc (el.size + sizeof(*ee)); - ee->element.size = el.size; - GNUNET_memcpy (&ee[1], el.data, el.size); - ee->element.data = &ee[1]; - ee->element.element_type = el.element_type; - ee->remote = GNUNET_NO; - ee->mutations = NULL; - ee->mutations_size = 0; - ee->element_hash = hash; - GNUNET_break (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_put ( - set->content->elements, - &ee->element_hash, - ee, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - } - else if (GNUNET_YES == - is_element_of_generation (ee, - set->current_generation, - set->excluded_generations, - set->excluded_generations_size)) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Client inserted element %s of size %u twice (ignored)\n", - GNUNET_h2s (&hash), - el.size); - - /* same element inserted twice */ - return; - } - - { - struct MutationEvent mut = { .generation = set->current_generation, - .added = GNUNET_YES }; - GNUNET_array_append (ee->mutations, ee->mutations_size, mut); - } - // FIXME: inline - intersection_add (set->state, - ee); -} - - -/** - * Perform a mutation on a set as specified by the @a msg - * - * @param set the set to mutate - * @param msg specification of what to change - */ -static void -execute_mutation (struct Set *set, const struct GNUNET_SET_ElementMessage *msg) -{ - switch (ntohs (msg->header.type)) - { - case GNUNET_MESSAGE_TYPE_SETI_ADD: // FIXME: inline! - execute_add (set, msg); - break; - default: - GNUNET_break (0); - } -} - - -/** - * Execute mutations that were delayed on a set because of - * pending operations. - * - * @param set the set to execute mutations on - */ -static void -execute_delayed_mutations (struct Set *set) -{ - struct PendingMutation *pm; - - if (0 != set->content->iterator_count) - return; /* still cannot do this */ - while (NULL != (pm = set->content->pending_mutations_head)) - { - GNUNET_CONTAINER_DLL_remove (set->content->pending_mutations_head, - set->content->pending_mutations_tail, - pm); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Executing pending mutation on %p.\n", - pm->set); - execute_mutation (pm->set, pm->msg); - GNUNET_free (pm->msg); - GNUNET_free (pm); - } -} - - /** * Send the next element of a set to the set's client. The next element is given by * the set's current hashmap iterator. The set's iterator will be set to NULL if there @@ -2542,7 +1932,8 @@ send_client_element (struct Set *set) * @param m message sent by the client */ static void -handle_client_iterate (void *cls, const struct GNUNET_MessageHeader *m) +handle_client_iterate (void *cls, + const struct GNUNET_MessageHeader *m) { struct ClientState *cs = cls; struct Set *set; @@ -2584,7 +1975,8 @@ handle_client_iterate (void *cls, const struct GNUNET_MessageHeader *m) * @param m message sent by the client */ static void -handle_client_create_set (void *cls, const struct GNUNET_SET_CreateMessage *msg) +handle_client_create_set (void *cls, + const struct GNUNET_SET_CreateMessage *msg) { struct ClientState *cs = cls; struct Set *set; @@ -2600,24 +1992,18 @@ handle_client_create_set (void *cls, const struct GNUNET_SET_CreateMessage *msg) return; } set = GNUNET_new (struct Set); - switch (ntohl (msg->operation)) { - case GNUNET_SET_OPERATION_INTERSECTION: - set->vt = _GSS_intersection_vt (); - break; + struct SetState *set_state; - case GNUNET_SET_OPERATION_UNION: - set->vt = _GSS_union_vt (); - break; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Intersection set created\n"); + set_state = GNUNET_new (struct SetState); + set_state->current_set_element_count = 0; - default: - GNUNET_free (set); - GNUNET_break (0); - GNUNET_SERVICE_client_drop (cs->client); - return; + set->state = set_state; } - set->operation = (enum GNUNET_SET_OperationType) ntohl (msg->operation); - set->state = intersection_set_create (); // FIXME: inline + + if (NULL == set->state) { /* initialization failed (i.e. out of memory) */ @@ -2627,7 +2013,8 @@ handle_client_create_set (void *cls, const struct GNUNET_SET_CreateMessage *msg) } set->content = GNUNET_new (struct SetContent); set->content->refcount = 1; - set->content->elements = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES); + set->content->elements = GNUNET_CONTAINER_multihashmap_create (1, + GNUNET_YES); set->cs = cs; cs->set = set; GNUNET_SERVICE_client_continue (cs->client); @@ -2679,17 +2066,21 @@ channel_new_cb (void *cls, struct Listener *listener = cls; struct Operation *op; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New incoming channel\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "New incoming channel\n"); op = GNUNET_new (struct Operation); op->listener = listener; op->peer = *source; op->channel = channel; op->mq = GNUNET_CADET_get_mq (op->channel); - op->salt = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX); + op->salt = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, + UINT32_MAX); op->timeout_task = GNUNET_SCHEDULER_add_delayed (INCOMING_CHANNEL_TIMEOUT, &incoming_timeout_cb, op); - GNUNET_CONTAINER_DLL_insert (listener->op_head, listener->op_tail, op); + GNUNET_CONTAINER_DLL_insert (listener->op_head, + listener->op_tail, + op); return op; } @@ -2711,7 +2102,8 @@ channel_new_cb (void *cls, * @param channel connection to the other end (henceforth invalid) */ static void -channel_end_cb (void *channel_ctx, const struct GNUNET_CADET_Channel *channel) +channel_end_cb (void *channel_ctx, + const struct GNUNET_CADET_Channel *channel) { struct Operation *op = channel_ctx; @@ -2725,12 +2117,13 @@ channel_end_cb (void *channel_ctx, const struct GNUNET_CADET_Channel *channel) * and be replaced by inlining more specific * logic in the various places where it is called. */ -void +static void _GSS_operation_destroy2 (struct Operation *op) { struct GNUNET_CADET_Channel *channel; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "channel_end_cb called\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "channel_end_cb called\n"); if (NULL != (channel = op->channel)) { /* This will free op; called conditionally as this helper function @@ -2744,9 +2137,22 @@ _GSS_operation_destroy2 (struct Operation *op) return; } if (NULL != op->set) - intersection_channel_death (op); // FIXME: inline + { + if (GNUNET_YES == op->state->channel_death_expected) + { + /* oh goodie, we are done! */ + send_client_done_and_destroy (op); + } + else + { + /* sorry, channel went down early, too bad. */ + _GSS_operation_destroy (op, + GNUNET_YES); + } + } else - _GSS_operation_destroy (op, GNUNET_YES); + _GSS_operation_destroy (op, + GNUNET_YES); GNUNET_free (op); } @@ -2781,7 +2187,8 @@ channel_window_cb (void *cls, * @param msg message sent by the client */ static void -handle_client_listen (void *cls, const struct GNUNET_SET_ListenMessage *msg) +handle_client_listen (void *cls, + const struct GNUNET_SET_ListenMessage *msg) { struct ClientState *cs = cls; struct GNUNET_MQ_MessageHandler cadet_handlers[] = @@ -2816,7 +2223,9 @@ handle_client_listen (void *cls, const struct GNUNET_SET_ListenMessage *msg) cs->listener = listener; listener->app_id = msg->app_id; listener->operation = (enum GNUNET_SET_OperationType) ntohl (msg->operation); - GNUNET_CONTAINER_DLL_insert (listener_head, listener_tail, listener); + GNUNET_CONTAINER_DLL_insert (listener_head, + listener_tail, + listener); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New listener created (op %u, port %s)\n", listener->operation, @@ -2840,7 +2249,8 @@ handle_client_listen (void *cls, const struct GNUNET_SET_ListenMessage *msg) * @param msg message sent by the client */ static void -handle_client_reject (void *cls, const struct GNUNET_SET_RejectMessage *msg) +handle_client_reject (void *cls, + const struct GNUNET_SET_RejectMessage *msg) { struct ClientState *cs = cls; struct Operation *op; @@ -2872,7 +2282,8 @@ handle_client_reject (void *cls, const struct GNUNET_SET_RejectMessage *msg) * @param msg message sent by the client */ static int -check_client_mutation (void *cls, const struct GNUNET_SET_ElementMessage *msg) +check_client_mutation (void *cls, + const struct GNUNET_SET_ElementMessage *msg) { /* NOTE: Technically, we should probably check with the block library whether the element we are given is well-formed */ @@ -2881,16 +2292,20 @@ check_client_mutation (void *cls, const struct GNUNET_SET_ElementMessage *msg) /** - * Called when a client wants to add or remove an element to a set it inhabits. + * Called when a client wants to add an element to a set it inhabits. * * @param cls client that sent the message * @param msg message sent by the client */ static void -handle_client_mutation (void *cls, const struct GNUNET_SET_ElementMessage *msg) +handle_client_set_add (void *cls, + const struct GNUNET_SET_ElementMessage *msg) { struct ClientState *cs = cls; struct Set *set; + struct GNUNET_SET_Element el; + struct ElementEntry *ee; + struct GNUNET_HashCode hash; if (NULL == (set = cs->set)) { @@ -2900,23 +2315,45 @@ handle_client_mutation (void *cls, const struct GNUNET_SET_ElementMessage *msg) return; } GNUNET_SERVICE_client_continue (cs->client); - - if (0 != set->content->iterator_count) + el.size = ntohs (msg->header.size) - sizeof(*msg); + el.data = &msg[1]; + el.element_type = ntohs (msg->element_type); + GNUNET_ISET_element_hash (&el, + &hash); + ee = GNUNET_CONTAINER_multihashmap_get (set->content->elements, + &hash); + if (NULL == ee) { - struct PendingMutation *pm; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Scheduling mutation on set\n"); - pm = GNUNET_new (struct PendingMutation); - pm->msg = - (struct GNUNET_SET_ElementMessage *) GNUNET_copy_message (&msg->header); - pm->set = set; - GNUNET_CONTAINER_DLL_insert_tail (set->content->pending_mutations_head, - set->content->pending_mutations_tail, - pm); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client inserts element %s of size %u\n", + GNUNET_h2s (&hash), + el.size); + ee = GNUNET_malloc (el.size + sizeof(*ee)); + ee->element.size = el.size; + GNUNET_memcpy (&ee[1], el.data, el.size); + ee->element.data = &ee[1]; + ee->element.element_type = el.element_type; + ee->remote = GNUNET_NO; + ee->mutations = NULL; + ee->mutations_size = 0; + ee->element_hash = hash; + GNUNET_break (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_put ( + set->content->elements, + &ee->element_hash, + ee, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client inserted element %s of size %u twice (ignored)\n", + GNUNET_h2s (&hash), + el.size); + /* same element inserted twice */ return; } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Executing mutation on set\n"); - execute_mutation (set, msg); + set->state->current_set_element_count++; } @@ -2929,24 +2366,13 @@ handle_client_mutation (void *cls, const struct GNUNET_SET_ElementMessage *msg) static void advance_generation (struct Set *set) { - struct GenerationRange r; - if (set->current_generation == set->content->latest_generation) { set->content->latest_generation++; set->current_generation++; return; } - GNUNET_assert (set->current_generation < set->content->latest_generation); - - r.start = set->current_generation + 1; - r.end = set->content->latest_generation + 1; - set->content->latest_generation = r.end; - set->current_generation = r.end; - GNUNET_array_append (set->excluded_generations, - set->excluded_generations_size, - r); } @@ -2960,7 +2386,8 @@ advance_generation (struct Set *set) * @return #GNUNET_OK if the message is well-formed */ static int -check_client_evaluate (void *cls, const struct GNUNET_SET_EvaluateMessage *msg) +check_client_evaluate (void *cls, + const struct GNUNET_SET_EvaluateMessage *msg) { /* FIXME: suboptimal, even if the context below could be NULL, there are malformed messages this does not check for... */ @@ -2977,7 +2404,8 @@ check_client_evaluate (void *cls, const struct GNUNET_SET_EvaluateMessage *msg) * @param msg message sent by the client */ static void -handle_client_evaluate (void *cls, const struct GNUNET_SET_EvaluateMessage *msg) +handle_client_evaluate (void *cls, + const struct GNUNET_SET_EvaluateMessage *msg) { struct ClientState *cs = cls; struct Operation *op = GNUNET_new (struct Operation); @@ -3010,7 +2438,8 @@ handle_client_evaluate (void *cls, const struct GNUNET_SET_EvaluateMessage *msg) GNUNET_SERVICE_client_drop (cs->client); return; } - op->salt = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX); + op->salt = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, + UINT32_MAX); op->peer = msg->target_peer; op->result_mode = ntohl (msg->result_mode); op->client_request_id = ntohl (msg->request_id); @@ -3025,7 +2454,9 @@ handle_client_evaluate (void *cls, const struct GNUNET_SET_EvaluateMessage *msg) op->set = set; op->generation_created = set->current_generation; advance_generation (set); - GNUNET_CONTAINER_DLL_insert (set->ops_head, set->ops_tail, op); + GNUNET_CONTAINER_DLL_insert (set->ops_head, + set->ops_tail, + op); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating new CADET channel to port %s for set operation type %u\n", GNUNET_h2s (&msg->app_id), @@ -3038,12 +2469,42 @@ handle_client_evaluate (void *cls, const struct GNUNET_SET_EvaluateMessage *msg) &channel_end_cb, cadet_handlers); op->mq = GNUNET_CADET_get_mq (op->channel); - op->state = intersection_evaluate (op, context); // FIXME: inline! - if (NULL == op->state) { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (cs->client); - return; + struct OperationState *state; + struct GNUNET_MQ_Envelope *ev; + struct OperationRequestMessage *msg; + + ev = GNUNET_MQ_msg_nested_mh (msg, + GNUNET_MESSAGE_TYPE_SETI_P2P_OPERATION_REQUEST, + context); + if (NULL == ev) + { + /* the context message is too large!? */ + GNUNET_break (0); + GNUNET_SERVICE_client_drop (cs->client); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Initiating intersection operation evaluation\n"); + state = GNUNET_new (struct OperationState); + /* we started the operation, thus we have to send the operation request */ + state->phase = PHASE_INITIAL; + state->my_element_count = op->set->state->current_set_element_count; + state->my_elements + = GNUNET_CONTAINER_multihashmap_create (state->my_element_count, + GNUNET_YES); + + msg->element_count = htonl (state->my_element_count); + GNUNET_MQ_send (op->mq, + ev); + state->phase = PHASE_COUNT_SENT; + if (NULL != opaque_context) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sent op request with context message\n"); + else + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sent op request without context message\n"); + op->state = state; } GNUNET_SERVICE_client_continue (cs->client); } @@ -3056,7 +2517,8 @@ handle_client_evaluate (void *cls, const struct GNUNET_SET_EvaluateMessage *msg) * @param msg the message */ static void -handle_client_cancel (void *cls, const struct GNUNET_SET_CancelMessage *msg) +handle_client_cancel (void *cls, + const struct GNUNET_SET_CancelMessage *msg) { struct ClientState *cs = cls; struct Set *set; @@ -3085,7 +2547,8 @@ handle_client_cancel (void *cls, const struct GNUNET_SET_CancelMessage *msg) * the other peer disconnecting. The client may not know about this * yet and try to cancel the (just barely non-existent) operation. * So this is not a hard error. - */GNUNET_log (GNUNET_ERROR_TYPE_INFO, + */// + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Client canceled non-existent op %u\n", (uint32_t) ntohl (msg->request_id)); } @@ -3109,7 +2572,8 @@ handle_client_cancel (void *cls, const struct GNUNET_SET_CancelMessage *msg) * @param msg the message */ static void -handle_client_accept (void *cls, const struct GNUNET_SET_AcceptMessage *msg) +handle_client_accept (void *cls, + const struct GNUNET_SET_AcceptMessage *msg) { struct ClientState *cs = cls; struct Set *set; @@ -3136,7 +2600,8 @@ handle_client_accept (void *cls, const struct GNUNET_SET_AcceptMessage *msg) cs, ntohl (msg->accept_reject_id), cs->listener); - ev = GNUNET_MQ_msg (result_message, GNUNET_MESSAGE_TYPE_SETI_RESULT); + ev = GNUNET_MQ_msg (result_message, + GNUNET_MESSAGE_TYPE_SETI_RESULT); result_message->request_id = msg->request_id; result_message->result_status = htons (GNUNET_SET_STATUS_FAILURE); GNUNET_MQ_send (set->cs->mq, ev); @@ -3163,12 +2628,34 @@ handle_client_accept (void *cls, const struct GNUNET_SET_AcceptMessage *msg) op->generation_created = set->current_generation; advance_generation (set); GNUNET_assert (NULL == op->state); - op->state = intersection_accept (op); // FIXME: inline - if (NULL == op->state) { - GNUNET_break (0); - GNUNET_SERVICE_client_drop (cs->client); - return; + struct OperationState *state; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Accepting set intersection operation\n"); + state = GNUNET_new (struct OperationState); + state->phase = PHASE_INITIAL; + state->my_element_count + = op->set->state->current_set_element_count; + state->my_elements + = GNUNET_CONTAINER_multihashmap_create ( + GNUNET_MIN (state->my_element_count, + op->remote_element_count), + GNUNET_YES); + op->state = state; + if (op->remote_element_count < state->my_element_count) + { + /* If the other peer (Alice) has fewer elements than us (Bob), + we just send the count as Alice should send the first BF */ + send_element_count (op); + state->phase = PHASE_COUNT_SENT; + } + else + { + /* We have fewer elements, so we start with the BF */ + begin_bf_exchange (op); + } + op->state = state; } /* Now allow CADET to continue, as we did not do this in #handle_incoming_msg (as we wanted to first see if the @@ -3196,8 +2683,10 @@ shutdown_task (void *cls) cadet = NULL; } } - GNUNET_STATISTICS_destroy (_GSS_statistics, GNUNET_YES); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "handled shutdown request\n"); + GNUNET_STATISTICS_destroy (_GSS_statistics, + GNUNET_YES); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "handled shutdown request\n"); } @@ -3217,8 +2706,10 @@ run (void *cls, /* FIXME: need to modify SERVICE (!) API to allow us to run a shutdown task *after* clients were forcefully disconnected! */ - GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); - _GSS_statistics = GNUNET_STATISTICS_create ("set", cfg); + GNUNET_SCHEDULER_add_shutdown (&shutdown_task, + NULL); + _GSS_statistics = GNUNET_STATISTICS_create ("seti", + cfg); cadet = GNUNET_CADET_connect (cfg); if (NULL == cadet) { @@ -3234,7 +2725,7 @@ run (void *cls, * Define "main" method using service macro. */ GNUNET_SERVICE_MAIN ( - "set", + "seti", GNUNET_SERVICE_OPTION_NONE, &run, &client_connect_cb, @@ -3244,7 +2735,7 @@ GNUNET_SERVICE_MAIN ( GNUNET_MESSAGE_TYPE_SETI_ACCEPT, struct GNUNET_SET_AcceptMessage, NULL), - GNUNET_MQ_hd_var_size (client_mutation, + GNUNET_MQ_hd_var_size (client_set_add, GNUNET_MESSAGE_TYPE_SETI_ADD, struct GNUNET_SET_ElementMessage, NULL), -- cgit v1.2.3 From 9cbc1f9c76ae5de5bcfdc15f935cd04200c0e013 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 19 Aug 2020 00:06:35 +0200 Subject: -fix FTBFS for seti --- src/include/gnunet_protocols.h | 5 + src/seti/gnunet-service-seti.c | 1204 ++++++++++++++++------------------------ 2 files changed, 472 insertions(+), 737 deletions(-) diff --git a/src/include/gnunet_protocols.h b/src/include/gnunet_protocols.h index e9a2b1c0e..4526b75d9 100644 --- a/src/include/gnunet_protocols.h +++ b/src/include/gnunet_protocols.h @@ -1854,6 +1854,11 @@ extern "C" { */ #define GNUNET_MESSAGE_TYPE_SETI_P2P_DONE 593 +/** + * Request to begin set intersection operation. + */ +#define GNUNET_MESSAGE_TYPE_SETI_P2P_OPERATION_REQUEST 594 + /******************************************************************************* * SET message types diff --git a/src/seti/gnunet-service-seti.c b/src/seti/gnunet-service-seti.c index 037181bde..7159a7ba2 100644 --- a/src/seti/gnunet-service-seti.c +++ b/src/seti/gnunet-service-seti.c @@ -25,6 +25,10 @@ */ #include "gnunet-service-seti_protocol.h" #include "gnunet_statistics_service.h" +#include "gnunet_cadet_service.h" +#include "gnunet_seti_service.h" +#include "gnunet_block_lib.h" +#include "seti.h" /** * How long do we hold on to an incoming channel if there is @@ -76,18 +80,6 @@ enum IntersectionOperationPhase }; -/** - * Implementation-specific set state. Used as opaque pointer, and - * specified further in the respective implementation. - */ -struct SetState; - -/** - * Implementation-specific set operation. Used as opaque pointer, and - * specified further in the respective implementation. - */ -struct OperationState; - /** * A set that supports a specific operation with other peers. */ @@ -119,7 +111,7 @@ struct ElementEntry * The actual element. The data for the element * should be allocated at the end of this struct. */ - struct GNUNET_SET_Element element; + struct GNUNET_SETI_Element element; /** * Hash of the element. For set union: Will be used to derive the @@ -179,6 +171,26 @@ struct ClientState */ struct Operation { + /** + * The identity of the requesting peer. Needs to + * be stored here as the op spec might not have been created yet. + */ + struct GNUNET_PeerIdentity peer; + + /** + * XOR of the keys of all of the elements (remaining) in my set. + * Always updated when elements are added or removed to + * @e my_elements. + */ + struct GNUNET_HashCode my_xor; + + /** + * XOR of the keys of all of the elements (remaining) in + * the other peer's set. Updated when we receive the + * other peer's Bloom filter. + */ + struct GNUNET_HashCode other_xor; + /** * Kept in a DLL of the listener, if @e listener is non-NULL. */ @@ -216,17 +228,31 @@ struct Operation struct Set *set; /** - * Operation-specific operation state. Note that the exact - * type depends on this being a union or intersection operation - * (and thus on @e vt). + * The bf we currently receive */ - struct OperationState *state; // FIXME: inline + struct GNUNET_CONTAINER_BloomFilter *remote_bf; /** - * The identity of the requesting peer. Needs to - * be stored here as the op spec might not have been created yet. + * BF of the set's element. */ - struct GNUNET_PeerIdentity peer; + struct GNUNET_CONTAINER_BloomFilter *local_bf; + + /** + * Remaining elements in the intersection operation. + * Maps element-id-hashes to 'elements in our set'. + */ + struct GNUNET_CONTAINER_MultiHashMap *my_elements; + + /** + * Iterator for sending the final set of @e my_elements to the client. + */ + struct GNUNET_CONTAINER_MultiHashMapIterator *full_result_iter; + + /** + * For multipart BF transmissions, we have to store the + * bloomfilter-data until we fully received it. + */ + char *bf_data; /** * Timeout task, if the incoming peer has not been accepted @@ -235,48 +261,68 @@ struct Operation struct GNUNET_SCHEDULER_Task *timeout_task; /** - * Salt to use for the operation. + * How many bytes of @e bf_data are valid? + */ + uint32_t bf_data_offset; + + /** + * Current element count contained within @e my_elements. + * (May differ briefly during initialization.) + */ + uint32_t my_element_count; + + /** + * size of the bloomfilter in @e bf_data. + */ + uint32_t bf_data_size; + + /** + * size of the bloomfilter + */ + uint32_t bf_bits_per_element; + + /** + * Salt currently used for BF construction (by us or the other peer, + * depending on where we are in the code). */ uint32_t salt; /** - * Remote peers element count + * Current state of the operation. */ - uint32_t remote_element_count; + enum IntersectionOperationPhase phase; /** - * ID used to identify an operation between service and client + * Generation in which the operation handle was created. */ - uint32_t client_request_id; + unsigned int generation_created; /** - * When are elements sent to the client, and which elements are sent? + * Did we send the client that we are done? */ - int return_intersection; + int client_done_sent; /** - * Lower bound for the set size, used only when - * byzantine mode is enabled. + * Set whenever we reach the state where the death of the + * channel is perfectly find and should NOT result in the + * operation being cancelled. */ - int byzantine_lower_bound; + int channel_death_expected; /** - * Always use delta operation instead of sending full sets, - * even it it's less efficient. + * Remote peers element count */ - int force_delta; + uint32_t remote_element_count; /** - * Always send full sets, even if delta operations would - * be more efficient. + * ID used to identify an operation between service and client */ - int force_full; + uint32_t client_request_id; /** - * #GNUNET_YES to fail operations where Byzantine faults - * are suspected + * When are elements sent to the client, and which elements are sent? */ - int byzantine; + int return_intersection; /** * Unique request id for the request from a remote peer, sent to the @@ -285,10 +331,6 @@ struct Operation */ uint32_t suggest_id; - /** - * Generation in which the operation handle was created. - */ - unsigned int generation_created; }; @@ -348,9 +390,10 @@ struct Set struct SetContent *content; /** - * Implementation-specific state. + * Number of currently valid elements in the set which have not been + * removed. */ - struct SetState *state; + uint32_t current_set_element_count; /** * Evaluate operations are held in a linked list. @@ -371,128 +414,6 @@ struct Set }; -/** - * State of an evaluate operation with another peer. - */ -struct OperationState -{ - /** - * The bf we currently receive - */ - struct GNUNET_CONTAINER_BloomFilter *remote_bf; - - /** - * BF of the set's element. - */ - struct GNUNET_CONTAINER_BloomFilter *local_bf; - - /** - * Remaining elements in the intersection operation. - * Maps element-id-hashes to 'elements in our set'. - */ - struct GNUNET_CONTAINER_MultiHashMap *my_elements; - - /** - * Iterator for sending the final set of @e my_elements to the client. - */ - struct GNUNET_CONTAINER_MultiHashMapIterator *full_result_iter; - - /** - * Evaluate operations are held in a linked list. - */ - struct OperationState *next; - - /** - * Evaluate operations are held in a linked list. - */ - struct OperationState *prev; - - /** - * For multipart BF transmissions, we have to store the - * bloomfilter-data until we fully received it. - */ - char *bf_data; - - /** - * XOR of the keys of all of the elements (remaining) in my set. - * Always updated when elements are added or removed to - * @e my_elements. - */ - struct GNUNET_HashCode my_xor; - - /** - * XOR of the keys of all of the elements (remaining) in - * the other peer's set. Updated when we receive the - * other peer's Bloom filter. - */ - struct GNUNET_HashCode other_xor; - - /** - * How many bytes of @e bf_data are valid? - */ - uint32_t bf_data_offset; - - /** - * Current element count contained within @e my_elements. - * (May differ briefly during initialization.) - */ - uint32_t my_element_count; - - /** - * size of the bloomfilter in @e bf_data. - */ - uint32_t bf_data_size; - - /** - * size of the bloomfilter - */ - uint32_t bf_bits_per_element; - - /** - * Salt currently used for BF construction (by us or the other peer, - * depending on where we are in the code). - */ - uint32_t salt; - - /** - * Current state of the operation. - */ - enum IntersectionOperationPhase phase; - - /** - * Generation in which the operation handle - * was created. - */ - unsigned int generation_created; - - /** - * Did we send the client that we are done? - */ - int client_done_sent; - - /** - * Set whenever we reach the state where the death of the - * channel is perfectly find and should NOT result in the - * operation being cancelled. - */ - int channel_death_expected; -}; - - -/** - * Extra state required for efficient set intersection. - * Merely tracks the total number of elements. - */ -struct SetState -{ - /** - * Number of currently valid elements in the set which have not been - * removed. - */ - uint32_t current_set_element_count; -}; - - /** * A listener is inhabited by a client, and waits for evaluation * requests from remote peers. @@ -540,10 +461,6 @@ struct Listener */ struct GNUNET_HashCode app_id; - /** - * The type of the operation. - */ - enum GNUNET_SET_OperationType operation; }; @@ -597,10 +514,10 @@ static uint32_t suggest_id; */ static void send_client_removed_element (struct Operation *op, - struct GNUNET_SET_Element *element) + struct GNUNET_SETI_Element *element) { struct GNUNET_MQ_Envelope *ev; - struct GNUNET_SET_ResultMessage *rm; + struct GNUNET_SETI_ResultMessage *rm; if (GNUNET_NO != op->return_intersection) return; /* Wrong mode for transmitting removed elements */ @@ -620,7 +537,7 @@ send_client_removed_element (struct Operation *op, GNUNET_break (0); return; } - rm->result_status = htons (GNUNET_SET_STATUS_DEL_LOCAL); + rm->result_status = htons (GNUNET_SETI_STATUS_DEL_LOCAL); rm->request_id = htonl (op->client_request_id); rm->element_type = element->element_type; GNUNET_memcpy (&rm[1], @@ -631,6 +548,21 @@ send_client_removed_element (struct Operation *op, } +/** + * Is element @a ee part of the set used by @a op? + * + * @param ee element to test + * @param op operation the defines the set and its generation + * @return #GNUNET_YES if the element is in the set, #GNUNET_NO if not + */ +static int +_GSS_is_element_of_operation (struct ElementEntry *ee, + struct Operation *op) +{ + return op->generation_created >= ee->generation_added; +} + + /** * Fills the "my_elements" hashmap with all relevant elements. * @@ -664,14 +596,14 @@ filtered_map_initialization (void *cls, /* Test if element is in other peer's bloomfilter */ GNUNET_BLOCK_mingle_hash (&ee->element_hash, - op->state->salt, + op->salt, &mutated_hash); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Testing mingled hash %s with salt %u\n", GNUNET_h2s (&mutated_hash), - op->state->salt); + op->salt); if (GNUNET_NO == - GNUNET_CONTAINER_bloomfilter_test (op->state->remote_bf, + GNUNET_CONTAINER_bloomfilter_test (op->remote_bf, &mutated_hash)) { /* remove this element */ @@ -683,16 +615,16 @@ filtered_map_initialization (void *cls, ee->element.size); return GNUNET_YES; } - op->state->my_element_count++; - GNUNET_CRYPTO_hash_xor (&op->state->my_xor, + op->my_element_count++; + GNUNET_CRYPTO_hash_xor (&op->my_xor, &ee->element_hash, - &op->state->my_xor); + &op->my_xor); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Filtered initialization of my_elements, adding %s:%u\n", GNUNET_h2s (&ee->element_hash), ee->element.size); GNUNET_break (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_put (op->state->my_elements, + GNUNET_CONTAINER_multihashmap_put (op->my_elements, &ee->element_hash, ee, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); @@ -720,27 +652,27 @@ iterator_bf_reduce (void *cls, struct GNUNET_HashCode mutated_hash; GNUNET_BLOCK_mingle_hash (&ee->element_hash, - op->state->salt, + op->salt, &mutated_hash); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Testing mingled hash %s with salt %u\n", GNUNET_h2s (&mutated_hash), - op->state->salt); + op->salt); if (GNUNET_NO == - GNUNET_CONTAINER_bloomfilter_test (op->state->remote_bf, + GNUNET_CONTAINER_bloomfilter_test (op->remote_bf, &mutated_hash)) { - GNUNET_break (0 < op->state->my_element_count); - op->state->my_element_count--; - GNUNET_CRYPTO_hash_xor (&op->state->my_xor, + GNUNET_break (0 < op->my_element_count); + op->my_element_count--; + GNUNET_CRYPTO_hash_xor (&op->my_xor, &ee->element_hash, - &op->state->my_xor); + &op->my_xor); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Bloom filter reduction of my_elements, removing %s:%u\n", GNUNET_h2s (&ee->element_hash), ee->element.size); GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_remove (op->state->my_elements, + GNUNET_CONTAINER_multihashmap_remove (op->my_elements, &ee->element_hash, ee)); send_client_removed_element (op, @@ -775,18 +707,196 @@ iterator_bf_create (void *cls, struct GNUNET_HashCode mutated_hash; GNUNET_BLOCK_mingle_hash (&ee->element_hash, - op->state->salt, + op->salt, &mutated_hash); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Initializing BF with hash %s with salt %u\n", GNUNET_h2s (&mutated_hash), - op->state->salt); - GNUNET_CONTAINER_bloomfilter_add (op->state->local_bf, + op->salt); + GNUNET_CONTAINER_bloomfilter_add (op->local_bf, &mutated_hash); return GNUNET_YES; } +/** + * Destroy the given operation. Used for any operation where both + * peers were known and that thus actually had a vt and channel. Must + * not be used for operations where 'listener' is still set and we do + * not know the other peer. + * + * Call the implementation-specific cancel function of the operation. + * Disconnects from the remote peer. Does not disconnect the client, + * as there may be multiple operations per set. + * + * @param op operation to destroy + */ +static void +_GSS_operation_destroy (struct Operation *op) +{ + struct Set *set = op->set; + struct GNUNET_CADET_Channel *channel; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Destroying operation %p\n", op); + GNUNET_assert (NULL == op->listener); + if (NULL != op->remote_bf) + { + GNUNET_CONTAINER_bloomfilter_free (op->remote_bf); + op->remote_bf = NULL; + } + if (NULL != op->local_bf) + { + GNUNET_CONTAINER_bloomfilter_free (op->local_bf); + op->local_bf = NULL; + } + if (NULL != op->my_elements) + { + GNUNET_CONTAINER_multihashmap_destroy (op->my_elements); + op->my_elements = NULL; + } + if (NULL != op->full_result_iter) + { + GNUNET_CONTAINER_multihashmap_iterator_destroy ( + op->full_result_iter); + op->full_result_iter = NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Destroying intersection op state done\n"); + if (NULL != set) + { + GNUNET_CONTAINER_DLL_remove (set->ops_head, + set->ops_tail, + op); + op->set = NULL; + } + if (NULL != op->context_msg) + { + GNUNET_free (op->context_msg); + op->context_msg = NULL; + } + if (NULL != (channel = op->channel)) + { + /* This will free op; called conditionally as this helper function + is also called from within the channel disconnect handler. */ + op->channel = NULL; + GNUNET_CADET_channel_destroy (channel); + } + /* We rely on the channel end handler to free 'op'. When 'op->channel' was NULL, + * there was a channel end handler that will free 'op' on the call stack. */ +} + + +/** + * This function probably should not exist + * and be replaced by inlining more specific + * logic in the various places where it is called. + */ +static void +_GSS_operation_destroy2 (struct Operation *op); + + +/** + * Destroy an incoming request from a remote peer + * + * @param op remote request to destroy + */ +static void +incoming_destroy (struct Operation *op) +{ + struct Listener *listener; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Destroying incoming operation %p\n", + op); + if (NULL != (listener = op->listener)) + { + GNUNET_CONTAINER_DLL_remove (listener->op_head, + listener->op_tail, + op); + op->listener = NULL; + } + if (NULL != op->timeout_task) + { + GNUNET_SCHEDULER_cancel (op->timeout_task); + op->timeout_task = NULL; + } + _GSS_operation_destroy2 (op); +} + + +/** + * Signal to the client that the operation has finished and + * destroy the operation. + * + * @param cls operation to destroy + */ +static void +send_client_done_and_destroy (void *cls) +{ + struct Operation *op = cls; + struct GNUNET_MQ_Envelope *ev; + struct GNUNET_SETI_ResultMessage *rm; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Intersection succeeded, sending DONE to local client\n"); + GNUNET_STATISTICS_update (_GSS_statistics, + "# Intersection operations succeeded", + 1, + GNUNET_NO); + ev = GNUNET_MQ_msg (rm, + GNUNET_MESSAGE_TYPE_SETI_RESULT); + rm->request_id = htonl (op->client_request_id); + rm->result_status = htons (GNUNET_SETI_STATUS_DONE); + rm->element_type = htons (0); + GNUNET_MQ_send (op->set->cs->mq, + ev); + _GSS_operation_destroy (op); +} + + +/** + * This function probably should not exist + * and be replaced by inlining more specific + * logic in the various places where it is called. + */ +static void +_GSS_operation_destroy2 (struct Operation *op) +{ + struct GNUNET_CADET_Channel *channel; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "channel_end_cb called\n"); + if (NULL != (channel = op->channel)) + { + /* This will free op; called conditionally as this helper function + is also called from within the channel disconnect handler. */ + op->channel = NULL; + GNUNET_CADET_channel_destroy (channel); + } + if (NULL != op->listener) + { + incoming_destroy (op); + return; + } + if (NULL != op->set) + { + if (GNUNET_YES == op->channel_death_expected) + { + /* oh goodie, we are done! */ + send_client_done_and_destroy (op); + } + else + { + /* sorry, channel went down early, too bad. */ + _GSS_operation_destroy (op); + } + } + else + _GSS_operation_destroy (op); + GNUNET_free (op); +} + + /** * Inform the client that the intersection operation has failed, * and proceed to destroy the evaluate operation. @@ -797,7 +907,7 @@ static void fail_intersection_operation (struct Operation *op) { struct GNUNET_MQ_Envelope *ev; - struct GNUNET_SET_ResultMessage *msg; + struct GNUNET_SETI_ResultMessage *msg; GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Intersection operation failed\n"); @@ -805,10 +915,10 @@ fail_intersection_operation (struct Operation *op) "# Intersection operations failed", 1, GNUNET_NO); - if (NULL != op->state->my_elements) + if (NULL != op->my_elements) { - GNUNET_CONTAINER_multihashmap_destroy (op->state->my_elements); - op->state->my_elements = NULL; + GNUNET_CONTAINER_multihashmap_destroy (op->my_elements); + op->my_elements = NULL; } ev = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SETI_RESULT); @@ -817,8 +927,7 @@ fail_intersection_operation (struct Operation *op) msg->element_type = htons (0); GNUNET_MQ_send (op->set->cs->mq, ev); - _GSS_operation_destroy (op, - GNUNET_YES); + _GSS_operation_destroy (op); } @@ -845,22 +954,22 @@ send_bloomfilter (struct Operation *op) potential and minimize overall bandwidth consumption. */ bf_elementbits = 2 + ceil (log2 ((double) (op->remote_element_count - / (double) op->state->my_element_count))); + / (double) op->my_element_count))); if (bf_elementbits < 1) bf_elementbits = 1; /* make sure k is not 0 */ /* optimize BF-size to ~50% of bits set */ - bf_size = ceil ((double) (op->state->my_element_count + bf_size = ceil ((double) (op->my_element_count * bf_elementbits / log (2))); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending Bloom filter (%u) of size %u bytes\n", (unsigned int) bf_elementbits, (unsigned int) bf_size); - op->state->local_bf = GNUNET_CONTAINER_bloomfilter_init (NULL, - bf_size, - bf_elementbits); - op->state->salt = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, - UINT32_MAX); - GNUNET_CONTAINER_multihashmap_iterate (op->state->my_elements, + op->local_bf = GNUNET_CONTAINER_bloomfilter_init (NULL, + bf_size, + bf_elementbits); + op->salt = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, + UINT32_MAX); + GNUNET_CONTAINER_multihashmap_iterate (op->my_elements, &iterator_bf_create, op); @@ -876,17 +985,17 @@ send_bloomfilter (struct Operation *op) chunk_size = bf_size; ev = GNUNET_MQ_msg_extra (msg, chunk_size, - GNUNET_MESSAGE_TYPE_SETI_INTERSECTION_P2P_BF); + GNUNET_MESSAGE_TYPE_SETI_P2P_BF); GNUNET_assert (GNUNET_SYSERR != GNUNET_CONTAINER_bloomfilter_get_raw_data ( - op->state->local_bf, + op->local_bf, (char *) &msg[1], bf_size)); - msg->sender_element_count = htonl (op->state->my_element_count); + msg->sender_element_count = htonl (op->my_element_count); msg->bloomfilter_total_length = htonl (bf_size); msg->bits_per_element = htonl (bf_elementbits); - msg->sender_mutator = htonl (op->state->salt); - msg->element_xor_hash = op->state->my_xor; + msg->sender_mutator = htonl (op->salt); + msg->element_xor_hash = op->my_xor; GNUNET_MQ_send (op->mq, ev); } else @@ -895,7 +1004,7 @@ send_bloomfilter (struct Operation *op) bf_data = GNUNET_malloc (bf_size); GNUNET_assert (GNUNET_SYSERR != GNUNET_CONTAINER_bloomfilter_get_raw_data ( - op->state->local_bf, + op->local_bf, bf_data, bf_size)); offset = 0; @@ -905,53 +1014,22 @@ send_bloomfilter (struct Operation *op) chunk_size = bf_size - offset; ev = GNUNET_MQ_msg_extra (msg, chunk_size, - GNUNET_MESSAGE_TYPE_SETI_INTERSECTION_P2P_BF); + GNUNET_MESSAGE_TYPE_SETI_P2P_BF); GNUNET_memcpy (&msg[1], &bf_data[offset], chunk_size); offset += chunk_size; - msg->sender_element_count = htonl (op->state->my_element_count); + msg->sender_element_count = htonl (op->my_element_count); msg->bloomfilter_total_length = htonl (bf_size); msg->bits_per_element = htonl (bf_elementbits); - msg->sender_mutator = htonl (op->state->salt); - msg->element_xor_hash = op->state->my_xor; + msg->sender_mutator = htonl (op->salt); + msg->element_xor_hash = op->my_xor; GNUNET_MQ_send (op->mq, ev); } GNUNET_free (bf_data); } - GNUNET_CONTAINER_bloomfilter_free (op->state->local_bf); - op->state->local_bf = NULL; -} - - -/** - * Signal to the client that the operation has finished and - * destroy the operation. - * - * @param cls operation to destroy - */ -static void -send_client_done_and_destroy (void *cls) -{ - struct Operation *op = cls; - struct GNUNET_MQ_Envelope *ev; - struct GNUNET_SET_ResultMessage *rm; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Intersection succeeded, sending DONE to local client\n"); - GNUNET_STATISTICS_update (_GSS_statistics, - "# Intersection operations succeeded", - 1, - GNUNET_NO); - ev = GNUNET_MQ_msg (rm, - GNUNET_MESSAGE_TYPE_SETI_RESULT); - rm->request_id = htonl (op->client_request_id); - rm->result_status = htons (GNUNET_SET_STATUS_DONE); - rm->element_type = htons (0); - GNUNET_MQ_send (op->set->cs->mq, - ev); - _GSS_operation_destroy (op, - GNUNET_YES); + GNUNET_CONTAINER_bloomfilter_free (op->local_bf); + op->local_bf = NULL; } @@ -970,8 +1048,8 @@ finished_local_operations (void *cls) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "DONE sent to other peer, now waiting for other end to close the channel\n"); - op->state->phase = PHASE_FINISHED; - op->state->channel_death_expected = GNUNET_YES; + op->phase = PHASE_FINISHED; + op->channel_death_expected = GNUNET_YES; } @@ -988,12 +1066,12 @@ send_p2p_done (struct Operation *op) struct GNUNET_MQ_Envelope *ev; struct IntersectionDoneMessage *idm; - GNUNET_assert (PHASE_MUST_SEND_DONE == op->state->phase); - GNUNET_assert (GNUNET_NO == op->state->channel_death_expected); + GNUNET_assert (PHASE_MUST_SEND_DONE == op->phase); + GNUNET_assert (GNUNET_NO == op->channel_death_expected); ev = GNUNET_MQ_msg (idm, - GNUNET_MESSAGE_TYPE_SETI_INTERSECTION_P2P_DONE); - idm->final_element_count = htonl (op->state->my_element_count); - idm->element_xor_hash = op->state->my_xor; + GNUNET_MESSAGE_TYPE_SETI_P2P_DONE); + idm->final_element_count = htonl (op->my_element_count); + idm->element_xor_hash = op->my_xor; GNUNET_MQ_notify_sent (ev, &finished_local_operations, op); @@ -1014,12 +1092,12 @@ send_remaining_elements (void *cls) const void *nxt; const struct ElementEntry *ee; struct GNUNET_MQ_Envelope *ev; - struct GNUNET_SET_ResultMessage *rm; - const struct GNUNET_SET_Element *element; + struct GNUNET_SETI_ResultMessage *rm; + const struct GNUNET_SETI_Element *element; int res; res = GNUNET_CONTAINER_multihashmap_iterator_next ( - op->state->full_result_iter, + op->full_result_iter, NULL, &nxt); if (GNUNET_NO == res) @@ -1027,14 +1105,14 @@ send_remaining_elements (void *cls) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending done and destroy because iterator ran out\n"); GNUNET_CONTAINER_multihashmap_iterator_destroy ( - op->state->full_result_iter); - op->state->full_result_iter = NULL; - if (PHASE_DONE_RECEIVED == op->state->phase) + op->full_result_iter); + op->full_result_iter = NULL; + if (PHASE_DONE_RECEIVED == op->phase) { - op->state->phase = PHASE_FINISHED; + op->phase = PHASE_FINISHED; send_client_done_and_destroy (op); } - else if (PHASE_MUST_SEND_DONE == op->state->phase) + else if (PHASE_MUST_SEND_DONE == op->phase) { send_p2p_done (op); } @@ -1055,7 +1133,7 @@ send_remaining_elements (void *cls) element->size, GNUNET_MESSAGE_TYPE_SETI_RESULT); GNUNET_assert (NULL != ev); - rm->result_status = htons (GNUNET_SET_STATUS_OK); + rm->result_status = htons (GNUNET_SETI_STATUS_ADD_LOCAL); rm->request_id = htonl (op->client_request_id); rm->element_type = element->element_type; GNUNET_memcpy (&rm[1], @@ -1088,15 +1166,15 @@ initialize_map_unfiltered (void *cls, if (GNUNET_NO == _GSS_is_element_of_operation (ee, op)) return GNUNET_YES; /* element not live in operation's generation */ - GNUNET_CRYPTO_hash_xor (&op->state->my_xor, + GNUNET_CRYPTO_hash_xor (&op->my_xor, &ee->element_hash, - &op->state->my_xor); + &op->my_xor); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Initial full initialization of my_elements, adding %s:%u\n", GNUNET_h2s (&ee->element_hash), ee->element.size); GNUNET_break (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_put (op->state->my_elements, + GNUNET_CONTAINER_multihashmap_put (op->my_elements, &ee->element_hash, ee, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); @@ -1118,10 +1196,10 @@ send_element_count (struct Operation *op) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending our element count (%u)\n", - op->state->my_element_count); + op->my_element_count); ev = GNUNET_MQ_msg (msg, - GNUNET_MESSAGE_TYPE_SETI_INTERSECTION_P2P_ELEMENT_INFO); - msg->sender_element_count = htonl (op->state->my_element_count); + GNUNET_MESSAGE_TYPE_SETI_P2P_ELEMENT_INFO); + msg->sender_element_count = htonl (op->my_element_count); GNUNET_MQ_send (op->mq, ev); } @@ -1135,7 +1213,7 @@ send_element_count (struct Operation *op) static void begin_bf_exchange (struct Operation *op) { - op->state->phase = PHASE_BF_EXCHANGE; + op->phase = PHASE_BF_EXCHANGE; GNUNET_CONTAINER_multihashmap_iterate (op->set->content->elements, &initialize_map_unfiltered, op); @@ -1157,28 +1235,22 @@ handle_intersection_p2p_element_info (void *cls, { struct Operation *op = cls; - if (GNUNET_SET_OPERATION_INTERSECTION != op->set->operation) - { - GNUNET_break_op (0); - fail_intersection_operation (op); - return; - } op->remote_element_count = ntohl (msg->sender_element_count); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received remote element count (%u), I have %u\n", op->remote_element_count, - op->state->my_element_count); - if (((PHASE_INITIAL != op->state->phase) && - (PHASE_COUNT_SENT != op->state->phase)) || - (op->state->my_element_count > op->remote_element_count) || - (0 == op->state->my_element_count) || + op->my_element_count); + if (((PHASE_INITIAL != op->phase) && + (PHASE_COUNT_SENT != op->phase)) || + (op->my_element_count > op->remote_element_count) || + (0 == op->my_element_count) || (0 == op->remote_element_count)) { GNUNET_break_op (0); fail_intersection_operation (op); return; } - GNUNET_break (NULL == op->state->remote_bf); + GNUNET_break (NULL == op->remote_bf); begin_bf_exchange (op); GNUNET_CADET_receive_done (op->channel); } @@ -1194,11 +1266,11 @@ process_bf (struct Operation *op) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received BF in phase %u, foreign count is %u, my element count is %u/%u\n", - op->state->phase, + op->phase, op->remote_element_count, - op->state->my_element_count, + op->my_element_count, GNUNET_CONTAINER_multihashmap_size (op->set->content->elements)); - switch (op->state->phase) + switch (op->phase) { case PHASE_INITIAL: GNUNET_break_op (0); @@ -1207,14 +1279,14 @@ process_bf (struct Operation *op) case PHASE_COUNT_SENT: /* This is the first BF being sent, build our initial map with filtering in place */ - op->state->my_element_count = 0; + op->my_element_count = 0; GNUNET_CONTAINER_multihashmap_iterate (op->set->content->elements, &filtered_map_initialization, op); break; case PHASE_BF_EXCHANGE: /* Update our set by reduction */ - GNUNET_CONTAINER_multihashmap_iterate (op->state->my_elements, + GNUNET_CONTAINER_multihashmap_iterate (op->my_elements, &iterator_bf_reduce, op); break; @@ -1231,35 +1303,35 @@ process_bf (struct Operation *op) fail_intersection_operation (op); return; } - GNUNET_CONTAINER_bloomfilter_free (op->state->remote_bf); - op->state->remote_bf = NULL; + GNUNET_CONTAINER_bloomfilter_free (op->remote_bf); + op->remote_bf = NULL; - if ((0 == op->state->my_element_count) || /* fully disjoint */ - ((op->state->my_element_count == op->remote_element_count) && - (0 == GNUNET_memcmp (&op->state->my_xor, - &op->state->other_xor)))) + if ((0 == op->my_element_count) || /* fully disjoint */ + ((op->my_element_count == op->remote_element_count) && + (0 == GNUNET_memcmp (&op->my_xor, + &op->other_xor)))) { /* we are done */ - op->state->phase = PHASE_MUST_SEND_DONE; + op->phase = PHASE_MUST_SEND_DONE; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Intersection succeeded, sending DONE to other peer\n"); - GNUNET_CONTAINER_bloomfilter_free (op->state->local_bf); - op->state->local_bf = NULL; - if (GNUNET_SET_RESULT_FULL == op->result_mode) + GNUNET_CONTAINER_bloomfilter_free (op->local_bf); + op->local_bf = NULL; + if (GNUNET_YES == op->return_intersection) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending full result set (%u elements)\n", - GNUNET_CONTAINER_multihashmap_size (op->state->my_elements)); - op->state->full_result_iter + GNUNET_CONTAINER_multihashmap_size (op->my_elements)); + op->full_result_iter = GNUNET_CONTAINER_multihashmap_iterator_create ( - op->state->my_elements); + op->my_elements); send_remaining_elements (op); return; } send_p2p_done (op); return; } - op->state->phase = PHASE_BF_EXCHANGE; + op->phase = PHASE_BF_EXCHANGE; send_bloomfilter (op); } @@ -1277,11 +1349,7 @@ check_intersection_p2p_bf (void *cls, { struct Operation *op = cls; - if (GNUNET_SET_OPERATION_INTERSECTION != op->set->operation) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } + (void) op; return GNUNET_OK; } @@ -1301,7 +1369,7 @@ handle_intersection_p2p_bf (void *cls, uint32_t chunk_size; uint32_t bf_bits_per_element; - switch (op->state->phase) + switch (op->phase) { case PHASE_INITIAL: GNUNET_break_op (0); @@ -1313,43 +1381,43 @@ handle_intersection_p2p_bf (void *cls, bf_size = ntohl (msg->bloomfilter_total_length); bf_bits_per_element = ntohl (msg->bits_per_element); chunk_size = htons (msg->header.size) - sizeof(struct BFMessage); - op->state->other_xor = msg->element_xor_hash; + op->other_xor = msg->element_xor_hash; if (bf_size == chunk_size) { - if (NULL != op->state->bf_data) + if (NULL != op->bf_data) { GNUNET_break_op (0); fail_intersection_operation (op); return; } /* single part, done here immediately */ - op->state->remote_bf + op->remote_bf = GNUNET_CONTAINER_bloomfilter_init ((const char *) &msg[1], bf_size, bf_bits_per_element); - op->state->salt = ntohl (msg->sender_mutator); + op->salt = ntohl (msg->sender_mutator); op->remote_element_count = ntohl (msg->sender_element_count); process_bf (op); break; } /* multipart chunk */ - if (NULL == op->state->bf_data) + if (NULL == op->bf_data) { /* first chunk, initialize */ - op->state->bf_data = GNUNET_malloc (bf_size); - op->state->bf_data_size = bf_size; - op->state->bf_bits_per_element = bf_bits_per_element; - op->state->bf_data_offset = 0; - op->state->salt = ntohl (msg->sender_mutator); + op->bf_data = GNUNET_malloc (bf_size); + op->bf_data_size = bf_size; + op->bf_bits_per_element = bf_bits_per_element; + op->bf_data_offset = 0; + op->salt = ntohl (msg->sender_mutator); op->remote_element_count = ntohl (msg->sender_element_count); } else { /* increment */ - if ((op->state->bf_data_size != bf_size) || - (op->state->bf_bits_per_element != bf_bits_per_element) || - (op->state->bf_data_offset + chunk_size > bf_size) || - (op->state->salt != ntohl (msg->sender_mutator)) || + if ((op->bf_data_size != bf_size) || + (op->bf_bits_per_element != bf_bits_per_element) || + (op->bf_data_offset + chunk_size > bf_size) || + (op->salt != ntohl (msg->sender_mutator)) || (op->remote_element_count != ntohl (msg->sender_element_count))) { GNUNET_break_op (0); @@ -1357,20 +1425,20 @@ handle_intersection_p2p_bf (void *cls, return; } } - GNUNET_memcpy (&op->state->bf_data[op->state->bf_data_offset], + GNUNET_memcpy (&op->bf_data[op->bf_data_offset], (const char *) &msg[1], chunk_size); - op->state->bf_data_offset += chunk_size; - if (op->state->bf_data_offset == bf_size) + op->bf_data_offset += chunk_size; + if (op->bf_data_offset == bf_size) { /* last chunk, run! */ - op->state->remote_bf - = GNUNET_CONTAINER_bloomfilter_init (op->state->bf_data, + op->remote_bf + = GNUNET_CONTAINER_bloomfilter_init (op->bf_data, bf_size, bf_bits_per_element); - GNUNET_free (op->state->bf_data); - op->state->bf_data = NULL; - op->state->bf_data_size = 0; + GNUNET_free (op->bf_data); + op->bf_data = NULL; + op->bf_data_size = 0; process_bf (op); } break; @@ -1400,17 +1468,17 @@ filter_all (void *cls, struct Operation *op = cls; struct ElementEntry *ee = value; - GNUNET_break (0 < op->state->my_element_count); - op->state->my_element_count--; - GNUNET_CRYPTO_hash_xor (&op->state->my_xor, + GNUNET_break (0 < op->my_element_count); + op->my_element_count--; + GNUNET_CRYPTO_hash_xor (&op->my_xor, &ee->element_hash, - &op->state->my_xor); + &op->my_xor); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Final reduction of my_elements, removing %s:%u\n", GNUNET_h2s (&ee->element_hash), ee->element.size); GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_remove (op->state->my_elements, + GNUNET_CONTAINER_multihashmap_remove (op->my_elements, &ee->element_hash, ee)); send_client_removed_element (op, @@ -1431,13 +1499,7 @@ handle_intersection_p2p_done (void *cls, { struct Operation *op = cls; - if (GNUNET_SET_OPERATION_INTERSECTION != op->set->operation) - { - GNUNET_break_op (0); - fail_intersection_operation (op); - return; - } - if (PHASE_BF_EXCHANGE != op->state->phase) + if (PHASE_BF_EXCHANGE != op->phase) { /* wrong phase to conclude? FIXME: Or should we allow this if the other peer has _initially_ already an empty set? */ @@ -1449,12 +1511,12 @@ handle_intersection_p2p_done (void *cls, { /* other peer determined empty set is the intersection, remove all elements */ - GNUNET_CONTAINER_multihashmap_iterate (op->state->my_elements, + GNUNET_CONTAINER_multihashmap_iterate (op->my_elements, &filter_all, op); } - if ((op->state->my_element_count != ntohl (idm->final_element_count)) || - (0 != GNUNET_memcmp (&op->state->my_xor, + if ((op->my_element_count != ntohl (idm->final_element_count)) || + (0 != GNUNET_memcmp (&op->my_xor, &idm->element_xor_hash))) { /* Other peer thinks we are done, but we disagree on the result! */ @@ -1464,22 +1526,22 @@ handle_intersection_p2p_done (void *cls, } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got IntersectionDoneMessage, have %u elements in intersection\n", - op->state->my_element_count); - op->state->phase = PHASE_DONE_RECEIVED; + op->my_element_count); + op->phase = PHASE_DONE_RECEIVED; GNUNET_CADET_receive_done (op->channel); - GNUNET_assert (GNUNET_NO == op->state->client_done_sent); - if (GNUNET_SET_RESULT_FULL == op->result_mode) + GNUNET_assert (GNUNET_NO == op->client_done_sent); + if (GNUNET_YES == op->return_intersection) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending full result set to client (%u elements)\n", - GNUNET_CONTAINER_multihashmap_size (op->state->my_elements)); - op->state->full_result_iter - = GNUNET_CONTAINER_multihashmap_iterator_create (op->state->my_elements); + GNUNET_CONTAINER_multihashmap_size (op->my_elements)); + op->full_result_iter + = GNUNET_CONTAINER_multihashmap_iterator_create (op->my_elements); send_remaining_elements (op); return; } - op->state->phase = PHASE_FINISHED; + op->phase = PHASE_FINISHED; send_client_done_and_destroy (op); } @@ -1506,122 +1568,6 @@ get_incoming (uint32_t id) } -/** - * Destroy an incoming request from a remote peer - * - * @param op remote request to destroy - */ -static void -incoming_destroy (struct Operation *op) -{ - struct Listener *listener; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Destroying incoming operation %p\n", - op); - if (NULL != (listener = op->listener)) - { - GNUNET_CONTAINER_DLL_remove (listener->op_head, listener->op_tail, op); - op->listener = NULL; - } - if (NULL != op->timeout_task) - { - GNUNET_SCHEDULER_cancel (op->timeout_task); - op->timeout_task = NULL; - } - _GSS_operation_destroy2 (op); -} - - -/** - * Is element @a ee part of the set used by @a op? - * - * @param ee element to test - * @param op operation the defines the set and its generation - * @return #GNUNET_YES if the element is in the set, #GNUNET_NO if not - */ -static int -_GSS_is_element_of_operation (struct ElementEntry *ee, - struct Operation *op) -{ - return op->generation_created >= ee->generation_added; -} - - -/** - * Destroy the given operation. Used for any operation where both - * peers were known and that thus actually had a vt and channel. Must - * not be used for operations where 'listener' is still set and we do - * not know the other peer. - * - * Call the implementation-specific cancel function of the operation. - * Disconnects from the remote peer. Does not disconnect the client, - * as there may be multiple operations per set. - * - * @param op operation to destroy - */ -static void -_GSS_operation_destroy (struct Operation *op) -{ - struct Set *set = op->set; - struct GNUNET_CADET_Channel *channel; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Destroying operation %p\n", op); - GNUNET_assert (NULL == op->listener); - if (NULL != op->state) - { - /* check if the op was canceled twice */ - GNUNET_assert (NULL != op->state); - if (NULL != op->state->remote_bf) - { - GNUNET_CONTAINER_bloomfilter_free (op->state->remote_bf); - op->state->remote_bf = NULL; - } - if (NULL != op->state->local_bf) - { - GNUNET_CONTAINER_bloomfilter_free (op->state->local_bf); - op->state->local_bf = NULL; - } - if (NULL != op->state->my_elements) - { - GNUNET_CONTAINER_multihashmap_destroy (op->state->my_elements); - op->state->my_elements = NULL; - } - if (NULL != op->state->full_result_iter) - { - GNUNET_CONTAINER_multihashmap_iterator_destroy ( - op->state->full_result_iter); - op->state->full_result_iter = NULL; - } - GNUNET_free (op->state); - op->state = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Destroying intersection op state done\n"); - } - if (NULL != set) - { - GNUNET_CONTAINER_DLL_remove (set->ops_head, - set->ops_tail, - op); - op->set = NULL; - } - if (NULL != op->context_msg) - { - GNUNET_free (op->context_msg); - op->context_msg = NULL; - } - if (NULL != (channel = op->channel)) - { - /* This will free op; called conditionally as this helper function - is also called from within the channel disconnect handler. */ - op->channel = NULL; - GNUNET_CADET_channel_destroy (channel); - } - /* We rely on the channel end handler to free 'op'. When 'op->channel' was NULL, - * there was a channel end handler that will free 'op' on the call stack. */ -} - - /** * Callback called when a client connects to the service. * @@ -1660,7 +1606,6 @@ destroy_elements_iterator (void *cls, { struct ElementEntry *ee = value; - GNUNET_free (ee->mutations); GNUNET_free (ee); return GNUNET_YES; } @@ -1691,19 +1636,7 @@ client_disconnect_cb (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Destroying client's set\n"); /* Destroy pending set operations */ while (NULL != set->ops_head) - _GSS_operation_destroy (set->ops_head, GNUNET_NO); - - /* Destroy operation-specific state */ - GNUNET_assert (NULL != set->state); - GNUNET_free (set->state); - - /* Clean up ongoing iterations */ - if (NULL != set->iter) - { - GNUNET_CONTAINER_multihashmap_iterator_destroy (set->iter); - set->iter = NULL; - set->iteration_id++; - } + _GSS_operation_destroy (set->ops_head); /* free set content (or at least decrement RC) */ set->content = NULL; @@ -1719,9 +1652,6 @@ client_disconnect_cb (void *cls, content->elements = NULL; GNUNET_free (content); } - GNUNET_free (set->excluded_generations); - set->excluded_generations = NULL; - GNUNET_free (set); } @@ -1777,20 +1707,14 @@ check_incoming_msg (void *cls, return GNUNET_SYSERR; } /* This should be equivalent to the previous condition, but can't hurt to check twice */ - if (NULL == op->listener) + if (NULL == listener) { GNUNET_break (0); return GNUNET_SYSERR; } - if (listener->operation != - (enum GNUNET_SET_OperationType) ntohl (msg->operation)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } nested_context = GNUNET_MQ_extract_nested_mh (msg); if ((NULL != nested_context) && - (ntohs (nested_context->size) > GNUNET_SET_CONTEXT_MESSAGE_MAX_SIZE)) + (ntohs (nested_context->size) > GNUNET_SETI_CONTEXT_MESSAGE_MAX_SIZE)) { GNUNET_break_op (0); return GNUNET_SYSERR; @@ -1824,7 +1748,7 @@ handle_incoming_msg (void *cls, struct Listener *listener = op->listener; const struct GNUNET_MessageHeader *nested_context; struct GNUNET_MQ_Envelope *env; - struct GNUNET_SET_RequestMessage *cmsg; + struct GNUNET_SETI_RequestMessage *cmsg; nested_context = GNUNET_MQ_extract_nested_mh (msg); /* Make a copy of the nested_context (application-specific context @@ -1835,8 +1759,7 @@ handle_incoming_msg (void *cls, op->remote_element_count = ntohl (msg->element_count); GNUNET_log ( GNUNET_ERROR_TYPE_DEBUG, - "Received P2P operation request (op %u, port %s) for active listener\n", - (uint32_t) ntohl (msg->operation), + "Received P2P operation request (port %s) for active listener\n", GNUNET_h2s (&op->listener->app_id)); GNUNET_assert (0 == op->suggest_id); if (0 == suggest_id) @@ -1862,110 +1785,6 @@ handle_incoming_msg (void *cls, } -/** - * Send the next element of a set to the set's client. The next element is given by - * the set's current hashmap iterator. The set's iterator will be set to NULL if there - * are no more elements in the set. The caller must ensure that the set's iterator is - * valid. - * - * The client will acknowledge each received element with a - * #GNUNET_MESSAGE_TYPE_SETI_ITER_ACK message. Our - * #handle_client_iter_ack() will then trigger the next transmission. - * Note that the #GNUNET_MESSAGE_TYPE_SETI_ITER_DONE is not acknowledged. - * - * @param set set that should send its next element to its client - */ -static void -send_client_element (struct Set *set) -{ - int ret; - struct ElementEntry *ee; - struct GNUNET_MQ_Envelope *ev; - struct GNUNET_SET_IterResponseMessage *msg; - - GNUNET_assert (NULL != set->iter); - do - { - ret = GNUNET_CONTAINER_multihashmap_iterator_next (set->iter, - NULL, - (const void **) &ee); - if (GNUNET_NO == ret) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Iteration on %p done.\n", set); - ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SETI_ITER_DONE); - GNUNET_CONTAINER_multihashmap_iterator_destroy (set->iter); - set->iter = NULL; - set->iteration_id++; - GNUNET_assert (set->content->iterator_count > 0); - set->content->iterator_count--; - execute_delayed_mutations (set); - GNUNET_MQ_send (set->cs->mq, ev); - return; - } - GNUNET_assert (NULL != ee); - } - while (GNUNET_NO == - is_element_of_generation (ee, - set->iter_generation, - set->excluded_generations, - set->excluded_generations_size)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending iteration element on %p.\n", - set); - ev = GNUNET_MQ_msg_extra (msg, - ee->element.size, - GNUNET_MESSAGE_TYPE_SETI_ITER_ELEMENT); - GNUNET_memcpy (&msg[1], ee->element.data, ee->element.size); - msg->element_type = htons (ee->element.element_type); - msg->iteration_id = htons (set->iteration_id); - GNUNET_MQ_send (set->cs->mq, ev); -} - - -/** - * Called when a client wants to iterate the elements of a set. - * Checks if we have a set associated with the client and if we - * can right now start an iteration. If all checks out, starts - * sending the elements of the set to the client. - * - * @param cls client that sent the message - * @param m message sent by the client - */ -static void -handle_client_iterate (void *cls, - const struct GNUNET_MessageHeader *m) -{ - struct ClientState *cs = cls; - struct Set *set; - - if (NULL == (set = cs->set)) - { - /* attempt to iterate over a non existing set */ - GNUNET_break (0); - GNUNET_SERVICE_client_drop (cs->client); - return; - } - if (NULL != set->iter) - { - /* Only one concurrent iterate-action allowed per set */ - GNUNET_break (0); - GNUNET_SERVICE_client_drop (cs->client); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Iterating set %p in gen %u with %u content elements\n", - (void *) set, - set->current_generation, - GNUNET_CONTAINER_multihashmap_size (set->content->elements)); - GNUNET_SERVICE_client_continue (cs->client); - set->content->iterator_count++; - set->iter = - GNUNET_CONTAINER_multihashmap_iterator_create (set->content->elements); - set->iter_generation = set->current_generation; - send_client_element (set); -} - - /** * Called when a client wants to create a new set. This is typically * the first request from a client, and includes the type of set @@ -1976,14 +1795,13 @@ handle_client_iterate (void *cls, */ static void handle_client_create_set (void *cls, - const struct GNUNET_SET_CreateMessage *msg) + const struct GNUNET_SETI_CreateMessage *msg) { struct ClientState *cs = cls; struct Set *set; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Client created new set (operation %u)\n", - (uint32_t) ntohl (msg->operation)); + "Client created new intersection set\n"); if (NULL != cs->set) { /* There can only be one set per client */ @@ -1992,25 +1810,6 @@ handle_client_create_set (void *cls, return; } set = GNUNET_new (struct Set); - { - struct SetState *set_state; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Intersection set created\n"); - set_state = GNUNET_new (struct SetState); - set_state->current_set_element_count = 0; - - set->state = set_state; - } - - - if (NULL == set->state) - { - /* initialization failed (i.e. out of memory) */ - GNUNET_free (set); - GNUNET_SERVICE_client_drop (cs->client); - return; - } set->content = GNUNET_new (struct SetContent); set->content->refcount = 1; set->content->elements = GNUNET_CONTAINER_multihashmap_create (1, @@ -2112,51 +1911,6 @@ channel_end_cb (void *channel_ctx, } -/** - * This function probably should not exist - * and be replaced by inlining more specific - * logic in the various places where it is called. - */ -static void -_GSS_operation_destroy2 (struct Operation *op) -{ - struct GNUNET_CADET_Channel *channel; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "channel_end_cb called\n"); - if (NULL != (channel = op->channel)) - { - /* This will free op; called conditionally as this helper function - is also called from within the channel disconnect handler. */ - op->channel = NULL; - GNUNET_CADET_channel_destroy (channel); - } - if (NULL != op->listener) - { - incoming_destroy (op); - return; - } - if (NULL != op->set) - { - if (GNUNET_YES == op->state->channel_death_expected) - { - /* oh goodie, we are done! */ - send_client_done_and_destroy (op); - } - else - { - /* sorry, channel went down early, too bad. */ - _GSS_operation_destroy (op, - GNUNET_YES); - } - } - else - _GSS_operation_destroy (op, - GNUNET_YES); - GNUNET_free (op); -} - - /** * Function called whenever an MQ-channel's transmission window size changes. * @@ -2188,11 +1942,11 @@ channel_window_cb (void *cls, */ static void handle_client_listen (void *cls, - const struct GNUNET_SET_ListenMessage *msg) + const struct GNUNET_SETI_ListenMessage *msg) { struct ClientState *cs = cls; - struct GNUNET_MQ_MessageHandler cadet_handlers[] = - { GNUNET_MQ_hd_var_size (incoming_msg, + struct GNUNET_MQ_MessageHandler cadet_handlers[] = { + GNUNET_MQ_hd_var_size (incoming_msg, GNUNET_MESSAGE_TYPE_SETI_P2P_OPERATION_REQUEST, struct OperationRequestMessage, NULL), @@ -2208,7 +1962,8 @@ handle_client_listen (void *cls, GNUNET_MESSAGE_TYPE_SETI_P2P_DONE, struct IntersectionDoneMessage, NULL), - GNUNET_MQ_handler_end () }; + GNUNET_MQ_handler_end () + }; struct Listener *listener; if (NULL != cs->listener) @@ -2222,13 +1977,11 @@ handle_client_listen (void *cls, listener->cs = cs; cs->listener = listener; listener->app_id = msg->app_id; - listener->operation = (enum GNUNET_SET_OperationType) ntohl (msg->operation); GNUNET_CONTAINER_DLL_insert (listener_head, listener_tail, listener); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "New listener created (op %u, port %s)\n", - listener->operation, + "New listener for set intersection created (port %s)\n", GNUNET_h2s (&listener->app_id)); listener->open_port = GNUNET_CADET_open_port (cadet, &msg->app_id, @@ -2250,7 +2003,7 @@ handle_client_listen (void *cls, */ static void handle_client_reject (void *cls, - const struct GNUNET_SET_RejectMessage *msg) + const struct GNUNET_SETI_RejectMessage *msg) { struct ClientState *cs = cls; struct Operation *op; @@ -2267,8 +2020,7 @@ handle_client_reject (void *cls, return; } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Peer request (op %u, app %s) rejected by client\n", - op->listener->operation, + "Peer request (app %s) rejected by client\n", GNUNET_h2s (&cs->listener->app_id)); _GSS_operation_destroy2 (op); GNUNET_SERVICE_client_continue (cs->client); @@ -2282,8 +2034,8 @@ handle_client_reject (void *cls, * @param msg message sent by the client */ static int -check_client_mutation (void *cls, - const struct GNUNET_SET_ElementMessage *msg) +check_client_set_add (void *cls, + const struct GNUNET_SETI_ElementMessage *msg) { /* NOTE: Technically, we should probably check with the block library whether the element we are given is well-formed */ @@ -2299,11 +2051,11 @@ check_client_mutation (void *cls, */ static void handle_client_set_add (void *cls, - const struct GNUNET_SET_ElementMessage *msg) + const struct GNUNET_SETI_ElementMessage *msg) { struct ClientState *cs = cls; struct Set *set; - struct GNUNET_SET_Element el; + struct GNUNET_SETI_Element el; struct ElementEntry *ee; struct GNUNET_HashCode hash; @@ -2318,7 +2070,7 @@ handle_client_set_add (void *cls, el.size = ntohs (msg->header.size) - sizeof(*msg); el.data = &msg[1]; el.element_type = ntohs (msg->element_type); - GNUNET_ISET_element_hash (&el, + GNUNET_SETI_element_hash (&el, &hash); ee = GNUNET_CONTAINER_multihashmap_get (set->content->elements, &hash); @@ -2334,8 +2086,6 @@ handle_client_set_add (void *cls, ee->element.data = &ee[1]; ee->element.element_type = el.element_type; ee->remote = GNUNET_NO; - ee->mutations = NULL; - ee->mutations_size = 0; ee->element_hash = hash; GNUNET_break (GNUNET_YES == GNUNET_CONTAINER_multihashmap_put ( @@ -2353,7 +2103,7 @@ handle_client_set_add (void *cls, /* same element inserted twice */ return; } - set->state->current_set_element_count++; + set->current_set_element_count++; } @@ -2387,7 +2137,7 @@ advance_generation (struct Set *set) */ static int check_client_evaluate (void *cls, - const struct GNUNET_SET_EvaluateMessage *msg) + const struct GNUNET_SETI_EvaluateMessage *msg) { /* FIXME: suboptimal, even if the context below could be NULL, there are malformed messages this does not check for... */ @@ -2405,7 +2155,7 @@ check_client_evaluate (void *cls, */ static void handle_client_evaluate (void *cls, - const struct GNUNET_SET_EvaluateMessage *msg) + const struct GNUNET_SETI_EvaluateMessage *msg) { struct ClientState *cs = cls; struct Operation *op = GNUNET_new (struct Operation); @@ -2441,12 +2191,7 @@ handle_client_evaluate (void *cls, op->salt = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX); op->peer = msg->target_peer; - op->result_mode = ntohl (msg->result_mode); op->client_request_id = ntohl (msg->request_id); - op->byzantine = msg->byzantine; - op->byzantine_lower_bound = msg->byzantine_lower_bound; - op->force_full = msg->force_full; - op->force_delta = msg->force_delta; context = GNUNET_MQ_extract_nested_mh (msg); /* Advance generation values, so that @@ -2458,9 +2203,8 @@ handle_client_evaluate (void *cls, set->ops_tail, op); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Creating new CADET channel to port %s for set operation type %u\n", - GNUNET_h2s (&msg->app_id), - set->operation); + "Creating new CADET channel to port %s for set intersection\n", + GNUNET_h2s (&msg->app_id)); op->channel = GNUNET_CADET_channel_create (cadet, op, &msg->target_peer, @@ -2470,7 +2214,6 @@ handle_client_evaluate (void *cls, cadet_handlers); op->mq = GNUNET_CADET_get_mq (op->channel); { - struct OperationState *state; struct GNUNET_MQ_Envelope *ev; struct OperationRequestMessage *msg; @@ -2486,25 +2229,23 @@ handle_client_evaluate (void *cls, } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Initiating intersection operation evaluation\n"); - state = GNUNET_new (struct OperationState); /* we started the operation, thus we have to send the operation request */ - state->phase = PHASE_INITIAL; - state->my_element_count = op->set->state->current_set_element_count; - state->my_elements - = GNUNET_CONTAINER_multihashmap_create (state->my_element_count, + op->phase = PHASE_INITIAL; + op->my_element_count = op->set->current_set_element_count; + op->my_elements + = GNUNET_CONTAINER_multihashmap_create (op->my_element_count, GNUNET_YES); - msg->element_count = htonl (state->my_element_count); + msg->element_count = htonl (op->my_element_count); GNUNET_MQ_send (op->mq, ev); - state->phase = PHASE_COUNT_SENT; - if (NULL != opaque_context) + op->phase = PHASE_COUNT_SENT; + if (NULL != context) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sent op request with context message\n"); else GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sent op request without context message\n"); - op->state = state; } GNUNET_SERVICE_client_continue (cs->client); } @@ -2518,7 +2259,7 @@ handle_client_evaluate (void *cls, */ static void handle_client_cancel (void *cls, - const struct GNUNET_SET_CancelMessage *msg) + const struct GNUNET_SETI_CancelMessage *msg) { struct ClientState *cs = cls; struct Set *set; @@ -2557,7 +2298,7 @@ handle_client_cancel (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client requested cancel for op %u\n", (uint32_t) ntohl (msg->request_id)); - _GSS_operation_destroy (op, GNUNET_YES); + _GSS_operation_destroy (op); } GNUNET_SERVICE_client_continue (cs->client); } @@ -2573,12 +2314,12 @@ handle_client_cancel (void *cls, */ static void handle_client_accept (void *cls, - const struct GNUNET_SET_AcceptMessage *msg) + const struct GNUNET_SETI_AcceptMessage *msg) { struct ClientState *cs = cls; struct Set *set; struct Operation *op; - struct GNUNET_SET_ResultMessage *result_message; + struct GNUNET_SETI_ResultMessage *result_message; struct GNUNET_MQ_Envelope *ev; struct Listener *listener; @@ -2603,7 +2344,7 @@ handle_client_accept (void *cls, ev = GNUNET_MQ_msg (result_message, GNUNET_MESSAGE_TYPE_SETI_RESULT); result_message->request_id = msg->request_id; - result_message->result_status = htons (GNUNET_SET_STATUS_FAILURE); + result_message->result_status = htons (GNUNET_SETI_STATUS_FAILURE); GNUNET_MQ_send (set->cs->mq, ev); GNUNET_SERVICE_client_continue (cs->client); return; @@ -2617,45 +2358,34 @@ handle_client_accept (void *cls, op->set = set; GNUNET_CONTAINER_DLL_insert (set->ops_head, set->ops_tail, op); op->client_request_id = ntohl (msg->request_id); - op->result_mode = ntohl (msg->result_mode); - op->byzantine = msg->byzantine; - op->byzantine_lower_bound = msg->byzantine_lower_bound; - op->force_full = msg->force_full; - op->force_delta = msg->force_delta; /* Advance generation values, so that future mutations do not interfer with the running operation. */ op->generation_created = set->current_generation; advance_generation (set); - GNUNET_assert (NULL == op->state); { - struct OperationState *state; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Accepting set intersection operation\n"); - state = GNUNET_new (struct OperationState); - state->phase = PHASE_INITIAL; - state->my_element_count - = op->set->state->current_set_element_count; - state->my_elements + op->phase = PHASE_INITIAL; + op->my_element_count + = op->set->current_set_element_count; + op->my_elements = GNUNET_CONTAINER_multihashmap_create ( - GNUNET_MIN (state->my_element_count, + GNUNET_MIN (op->my_element_count, op->remote_element_count), GNUNET_YES); - op->state = state; - if (op->remote_element_count < state->my_element_count) + if (op->remote_element_count < op->my_element_count) { /* If the other peer (Alice) has fewer elements than us (Bob), we just send the count as Alice should send the first BF */ send_element_count (op); - state->phase = PHASE_COUNT_SENT; + op->phase = PHASE_COUNT_SENT; } else { /* We have fewer elements, so we start with the BF */ begin_bf_exchange (op); } - op->state = state; } /* Now allow CADET to continue, as we did not do this in #handle_incoming_msg (as we wanted to first see if the @@ -2733,31 +2463,31 @@ GNUNET_SERVICE_MAIN ( NULL, GNUNET_MQ_hd_fixed_size (client_accept, GNUNET_MESSAGE_TYPE_SETI_ACCEPT, - struct GNUNET_SET_AcceptMessage, + struct GNUNET_SETI_AcceptMessage, NULL), GNUNET_MQ_hd_var_size (client_set_add, GNUNET_MESSAGE_TYPE_SETI_ADD, - struct GNUNET_SET_ElementMessage, + struct GNUNET_SETI_ElementMessage, NULL), GNUNET_MQ_hd_fixed_size (client_create_set, GNUNET_MESSAGE_TYPE_SETI_CREATE, - struct GNUNET_SET_CreateMessage, + struct GNUNET_SETI_CreateMessage, NULL), GNUNET_MQ_hd_var_size (client_evaluate, GNUNET_MESSAGE_TYPE_SETI_EVALUATE, - struct GNUNET_SET_EvaluateMessage, + struct GNUNET_SETI_EvaluateMessage, NULL), GNUNET_MQ_hd_fixed_size (client_listen, GNUNET_MESSAGE_TYPE_SETI_LISTEN, - struct GNUNET_SET_ListenMessage, + struct GNUNET_SETI_ListenMessage, NULL), GNUNET_MQ_hd_fixed_size (client_reject, GNUNET_MESSAGE_TYPE_SETI_REJECT, - struct GNUNET_SET_RejectMessage, + struct GNUNET_SETI_RejectMessage, NULL), GNUNET_MQ_hd_fixed_size (client_cancel, GNUNET_MESSAGE_TYPE_SETI_CANCEL, - struct GNUNET_SET_CancelMessage, + struct GNUNET_SETI_CancelMessage, NULL), GNUNET_MQ_handler_end ()); -- cgit v1.2.3 From 62853641f7ddb1e1b52b105c48039b3c65f36a32 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 19 Aug 2020 00:07:09 +0200 Subject: -fix FTBFS for seti --- src/seti/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/seti/Makefile.am b/src/seti/Makefile.am index d96ffff03..b4980b670 100644 --- a/src/seti/Makefile.am +++ b/src/seti/Makefile.am @@ -70,7 +70,7 @@ test_seti_api_SOURCES = \ test_seti_api_LDADD = \ $(top_builddir)/src/util/libgnunetutil.la \ $(top_builddir)/src/testing/libgnunettesting.la \ - libgnunetset.la + libgnunetseti.la plugin_LTLIBRARIES = \ libgnunet_plugin_block_seti_test.la -- cgit v1.2.3 From 076acb9981bc141c7905e0dcf3f9fcda3497a8aa Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 19 Aug 2020 00:24:48 +0200 Subject: -fix test FTBFS --- src/seti/test_seti_api.c | 224 ++++++++++++++++++----------------------------- 1 file changed, 86 insertions(+), 138 deletions(-) diff --git a/src/seti/test_seti_api.c b/src/seti/test_seti_api.c index 42dedb846..22a3a06e5 100644 --- a/src/seti/test_seti_api.c +++ b/src/seti/test_seti_api.c @@ -19,7 +19,7 @@ */ /** - * @file set/test_set_intersection_result_full.c + * @file set/test_seti_api.c * @brief testcase for full result mode of the intersection set operation * @author Christian Fuchs * @author Christian Grothoff @@ -27,7 +27,7 @@ #include "platform.h" #include "gnunet_util_lib.h" #include "gnunet_testing_lib.h" -#include "gnunet_set_service.h" +#include "gnunet_seti_service.h" static int ret; @@ -36,28 +36,26 @@ static struct GNUNET_PeerIdentity local_id; static struct GNUNET_HashCode app_id; -static struct GNUNET_SET_Handle *set1; +static struct GNUNET_SETI_Handle *set1; -static struct GNUNET_SET_Handle *set2; +static struct GNUNET_SETI_Handle *set2; -static struct GNUNET_SET_ListenHandle *listen_handle; +static struct GNUNET_SETI_ListenHandle *listen_handle; static const struct GNUNET_CONFIGURATION_Handle *config; -static int iter_count; - static struct GNUNET_SCHEDULER_Task *tt; -static struct GNUNET_SET_OperationHandle *oh1; +static struct GNUNET_SETI_OperationHandle *oh1; -static struct GNUNET_SET_OperationHandle *oh2; +static struct GNUNET_SETI_OperationHandle *oh2; static void result_cb_set1 (void *cls, - const struct GNUNET_SET_Element *element, + const struct GNUNET_SETI_Element *element, uint64_t current_size, - enum GNUNET_SET_Status status) + enum GNUNET_SETI_Status status) { static int count; @@ -66,19 +64,17 @@ result_cb_set1 (void *cls, status); switch (status) { - case GNUNET_SET_STATUS_OK: + case GNUNET_SETI_STATUS_ADD_LOCAL: count++; break; - - case GNUNET_SET_STATUS_FAILURE: + case GNUNET_SETI_STATUS_FAILURE: oh1 = NULL; ret = 1; break; - - case GNUNET_SET_STATUS_DONE: + case GNUNET_SETI_STATUS_DONE: oh1 = NULL; GNUNET_assert (1 == count); - GNUNET_SET_destroy (set1); + GNUNET_SETI_destroy (set1); set1 = NULL; if (NULL == set2) GNUNET_SCHEDULER_shutdown (); @@ -92,9 +88,9 @@ result_cb_set1 (void *cls, static void result_cb_set2 (void *cls, - const struct GNUNET_SET_Element *element, + const struct GNUNET_SETI_Element *element, uint64_t current_size, - enum GNUNET_SET_Status status) + enum GNUNET_SETI_Status status) { static int count; @@ -103,24 +99,21 @@ result_cb_set2 (void *cls, status); switch (status) { - case GNUNET_SET_STATUS_OK: + case GNUNET_SETI_STATUS_ADD_LOCAL: count++; break; - - case GNUNET_SET_STATUS_FAILURE: + case GNUNET_SETI_STATUS_FAILURE: oh2 = NULL; ret = 1; break; - - case GNUNET_SET_STATUS_DONE: + case GNUNET_SETI_STATUS_DONE: oh2 = NULL; GNUNET_assert (1 == count); - GNUNET_SET_destroy (set2); + GNUNET_SETI_destroy (set2); set2 = NULL; if (NULL == set1) GNUNET_SCHEDULER_shutdown (); break; - default: GNUNET_assert (0); } @@ -131,19 +124,23 @@ static void listen_cb (void *cls, const struct GNUNET_PeerIdentity *other_peer, const struct GNUNET_MessageHeader *context_msg, - struct GNUNET_SET_Request *request) + struct GNUNET_SETI_Request *request) { + struct GNUNET_SETI_Option opts[] = { + { .type = GNUNET_SETI_OPTION_RETURN_INTERSECTION }, + { .type = GNUNET_SETI_OPTION_END } + }; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "starting intersection by accepting and committing\n"); GNUNET_assert (NULL != context_msg); GNUNET_assert (ntohs (context_msg->type) == GNUNET_MESSAGE_TYPE_DUMMY); - oh2 = GNUNET_SET_accept (request, - GNUNET_SET_RESULT_FULL, - (struct GNUNET_SET_Option[]) { 0 }, - &result_cb_set2, - NULL); - GNUNET_SET_commit (oh2, - set2); + oh2 = GNUNET_SETI_accept (request, + opts, + &result_cb_set2, + NULL); + GNUNET_SETI_commit (oh2, + set2); } @@ -156,25 +153,27 @@ static void start (void *cls) { struct GNUNET_MessageHeader context_msg; + struct GNUNET_SETI_Option opts[] = { + { .type = GNUNET_SETI_OPTION_RETURN_INTERSECTION }, + { .type = GNUNET_SETI_OPTION_END } + }; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "starting listener\n"); - context_msg.size = htons (sizeof context_msg); + context_msg.size = htons (sizeof (context_msg)); context_msg.type = htons (GNUNET_MESSAGE_TYPE_DUMMY); - listen_handle = GNUNET_SET_listen (config, - GNUNET_SET_OPERATION_INTERSECTION, - &app_id, - &listen_cb, - NULL); - oh1 = GNUNET_SET_prepare (&local_id, - &app_id, - &context_msg, - GNUNET_SET_RESULT_FULL, - (struct GNUNET_SET_Option[]) { 0 }, - &result_cb_set1, - NULL); - GNUNET_SET_commit (oh1, - set1); + listen_handle = GNUNET_SETI_listen (config, + &app_id, + &listen_cb, + NULL); + oh1 = GNUNET_SETI_prepare (&local_id, + &app_id, + &context_msg, + opts, + &result_cb_set1, + NULL); + GNUNET_SETI_commit (oh1, + set1); } @@ -186,29 +185,29 @@ start (void *cls) static void init_set2 (void *cls) { - struct GNUNET_SET_Element element; + struct GNUNET_SETI_Element element; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "initializing set 2\n"); element.element_type = 0; element.data = "hello"; element.size = strlen (element.data); - GNUNET_SET_add_element (set2, - &element, - NULL, - NULL); + GNUNET_SETI_add_element (set2, + &element, + NULL, + NULL); element.data = "quux"; element.size = strlen (element.data); - GNUNET_SET_add_element (set2, - &element, - NULL, - NULL); + GNUNET_SETI_add_element (set2, + &element, + NULL, + NULL); element.data = "baz"; element.size = strlen (element.data); - GNUNET_SET_add_element (set2, - &element, - &start, - NULL); + GNUNET_SETI_add_element (set2, + &element, + &start, + NULL); } @@ -218,71 +217,23 @@ init_set2 (void *cls) static void init_set1 (void) { - struct GNUNET_SET_Element element; + struct GNUNET_SETI_Element element; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "initializing set 1\n"); element.element_type = 0; element.data = "hello"; element.size = strlen (element.data); - GNUNET_SET_add_element (set1, - &element, - NULL, - NULL); - element.data = "bar"; - element.size = strlen (element.data); - GNUNET_SET_add_element (set1, - &element, - &init_set2, - NULL); -} - - -static int -iter_cb (void *cls, - const struct GNUNET_SET_Element *element) -{ - if (NULL == element) - { - GNUNET_assert (iter_count == 3); - GNUNET_SET_destroy (cls); - return GNUNET_YES; - } - iter_count++; - return GNUNET_YES; -} - - -static void -test_iter () -{ - struct GNUNET_SET_Element element; - struct GNUNET_SET_Handle *iter_set; - - iter_set = GNUNET_SET_create (config, - GNUNET_SET_OPERATION_INTERSECTION); - element.element_type = 0; - element.data = "hello"; - element.size = strlen (element.data); - GNUNET_SET_add_element (iter_set, - &element, - NULL, - NULL); + GNUNET_SETI_add_element (set1, + &element, + NULL, + NULL); element.data = "bar"; element.size = strlen (element.data); - GNUNET_SET_add_element (iter_set, - &element, - NULL, - NULL); - element.data = "quux"; - element.size = strlen (element.data); - GNUNET_SET_add_element (iter_set, - &element, - NULL, - NULL); - GNUNET_SET_iterate (iter_set, - &iter_cb, - iter_set); + GNUNET_SETI_add_element (set1, + &element, + &init_set2, + NULL); } @@ -301,27 +252,27 @@ do_shutdown (void *cls) } if (NULL != oh1) { - GNUNET_SET_operation_cancel (oh1); + GNUNET_SETI_operation_cancel (oh1); oh1 = NULL; } if (NULL != oh2) { - GNUNET_SET_operation_cancel (oh2); + GNUNET_SETI_operation_cancel (oh2); oh2 = NULL; } if (NULL != set1) { - GNUNET_SET_destroy (set1); + GNUNET_SETI_destroy (set1); set1 = NULL; } if (NULL != set2) { - GNUNET_SET_destroy (set2); + GNUNET_SETI_destroy (set2); set2 = NULL; } if (NULL != listen_handle) { - GNUNET_SET_listen_cancel (listen_handle); + GNUNET_SETI_listen_cancel (listen_handle); listen_handle = NULL; } } @@ -359,20 +310,16 @@ run (void *cls, config = cfg; GNUNET_TESTING_peer_get_identity (peer, &local_id); - if (0) - test_iter (); - - tt = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply ( - GNUNET_TIME_UNIT_SECONDS, 5), - &timeout_fail, - NULL); + tt = GNUNET_SCHEDULER_add_delayed ( + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, + 5), + &timeout_fail, + NULL); GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); - set1 = GNUNET_SET_create (cfg, - GNUNET_SET_OPERATION_INTERSECTION); - set2 = GNUNET_SET_create (cfg, - GNUNET_SET_OPERATION_INTERSECTION); + set1 = GNUNET_SETI_create (cfg); + set2 = GNUNET_SETI_create (cfg); GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, &app_id); @@ -385,9 +332,10 @@ int main (int argc, char **argv) { - if (0 != GNUNET_TESTING_peer_run ("test_set_intersection_result_full", - "test_set.conf", - &run, NULL)) + if (0 != GNUNET_TESTING_peer_run ("test_seti_api", + "test_seti.conf", + &run, + NULL)) return 1; return ret; } -- cgit v1.2.3 From 1eda2878cf1efef694c911c318427f14ecbdf8f3 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 19 Aug 2020 00:46:39 +0200 Subject: -fix set/seti migration issues --- src/include/gnunet_block_lib.h | 13 ++++++++++++ src/seti/gnunet-service-seti.c | 20 +++++++++++++++--- src/seti/plugin_block_seti_test.c | 44 +++++++++++++++++++-------------------- src/seti/seti_api.c | 8 +++---- src/seti/test_seti.conf | 3 +-- src/setu/plugin_block_setu_test.c | 44 +++++++++++++++++++-------------------- 6 files changed, 79 insertions(+), 53 deletions(-) diff --git a/src/include/gnunet_block_lib.h b/src/include/gnunet_block_lib.h index 18ca6f63f..73b51252e 100644 --- a/src/include/gnunet_block_lib.h +++ b/src/include/gnunet_block_lib.h @@ -137,6 +137,19 @@ enum GNUNET_BLOCK_Type * Contains either special marker elements or a nested block. */ GNUNET_BLOCK_TYPE_CONSENSUS_ELEMENT = 25, + + /** + * Block for testing set intersection. If first byte of the block + * is non-zero, the block is considered invalid. + */ + GNUNET_BLOCK_TYPE_SETI_TEST = 24, + + /** + * Block for testing set union. If first byte of the block + * is non-zero, the block is considered invalid. + */ + GNUNET_BLOCK_TYPE_SETU_TEST = 24, + }; diff --git a/src/seti/gnunet-service-seti.c b/src/seti/gnunet-service-seti.c index 7159a7ba2..618d53128 100644 --- a/src/seti/gnunet-service-seti.c +++ b/src/seti/gnunet-service-seti.c @@ -519,8 +519,11 @@ send_client_removed_element (struct Operation *op, struct GNUNET_MQ_Envelope *ev; struct GNUNET_SETI_ResultMessage *rm; - if (GNUNET_NO != op->return_intersection) + if (GNUNET_YES == op->return_intersection) + { + GNUNET_break (0); return; /* Wrong mode for transmitting removed elements */ + } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending removed element (size %u) to client\n", element->size); @@ -1096,6 +1099,11 @@ send_remaining_elements (void *cls) const struct GNUNET_SETI_Element *element; int res; + if (GNUNET_NO == op->return_intersection) + { + GNUNET_break (0); + return; /* Wrong mode for transmitting removed elements */ + } res = GNUNET_CONTAINER_multihashmap_iterator_next ( op->full_result_iter, NULL, @@ -2191,6 +2199,7 @@ handle_client_evaluate (void *cls, op->salt = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX); op->peer = msg->target_peer; + op->return_intersection = htonl (msg->return_intersection); op->client_request_id = ntohl (msg->request_id); context = GNUNET_MQ_extract_nested_mh (msg); @@ -2354,9 +2363,14 @@ handle_client_accept (void *cls, (uint32_t) ntohl (msg->accept_reject_id)); listener = op->listener; op->listener = NULL; - GNUNET_CONTAINER_DLL_remove (listener->op_head, listener->op_tail, op); + op->return_intersection = htonl (msg->return_intersection); + GNUNET_CONTAINER_DLL_remove (listener->op_head, + listener->op_tail, + op); op->set = set; - GNUNET_CONTAINER_DLL_insert (set->ops_head, set->ops_tail, op); + GNUNET_CONTAINER_DLL_insert (set->ops_head, + set->ops_tail, + op); op->client_request_id = ntohl (msg->request_id); /* Advance generation values, so that future mutations do not diff --git a/src/seti/plugin_block_seti_test.c b/src/seti/plugin_block_seti_test.c index 1de086092..55cf31bea 100644 --- a/src/seti/plugin_block_seti_test.c +++ b/src/seti/plugin_block_seti_test.c @@ -19,7 +19,7 @@ */ /** - * @file set/plugin_block_set_test.c + * @file seti/plugin_block_seti_test.c * @brief set test block, recognizes elements with non-zero first byte as invalid * @author Christian Grothoff */ @@ -46,16 +46,16 @@ * @return characterization of result */ static enum GNUNET_BLOCK_EvaluationResult -block_plugin_set_test_evaluate (void *cls, - struct GNUNET_BLOCK_Context *ctx, - enum GNUNET_BLOCK_Type type, - struct GNUNET_BLOCK_Group *group, - enum GNUNET_BLOCK_EvaluationOptions eo, - const struct GNUNET_HashCode *query, - const void *xquery, - size_t xquery_size, - const void *reply_block, - size_t reply_block_size) +block_plugin_seti_test_evaluate (void *cls, + struct GNUNET_BLOCK_Context *ctx, + enum GNUNET_BLOCK_Type type, + struct GNUNET_BLOCK_Group *group, + enum GNUNET_BLOCK_EvaluationOptions eo, + const struct GNUNET_HashCode *query, + const void *xquery, + size_t xquery_size, + const void *reply_block, + size_t reply_block_size) { if ((NULL == reply_block) || (reply_block_size == 0) || @@ -77,11 +77,11 @@ block_plugin_set_test_evaluate (void *cls, * (or if extracting a key from a block of this type does not work) */ static int -block_plugin_set_test_get_key (void *cls, - enum GNUNET_BLOCK_Type type, - const void *block, - size_t block_size, - struct GNUNET_HashCode *key) +block_plugin_seti_test_get_key (void *cls, + enum GNUNET_BLOCK_Type type, + const void *block, + size_t block_size, + struct GNUNET_HashCode *key) { return GNUNET_SYSERR; } @@ -91,17 +91,17 @@ block_plugin_set_test_get_key (void *cls, * Entry point for the plugin. */ void * -libgnunet_plugin_block_set_test_init (void *cls) +libgnunet_plugin_block_seti_test_init (void *cls) { static enum GNUNET_BLOCK_Type types[] = { - GNUNET_BLOCK_TYPE_SET_TEST, + GNUNET_BLOCK_TYPE_SETI_TEST, GNUNET_BLOCK_TYPE_ANY /* end of list */ }; struct GNUNET_BLOCK_PluginFunctions *api; api = GNUNET_new (struct GNUNET_BLOCK_PluginFunctions); - api->evaluate = &block_plugin_set_test_evaluate; - api->get_key = &block_plugin_set_test_get_key; + api->evaluate = &block_plugin_seti_test_evaluate; + api->get_key = &block_plugin_seti_test_get_key; api->types = types; return api; } @@ -111,7 +111,7 @@ libgnunet_plugin_block_set_test_init (void *cls) * Exit point from the plugin. */ void * -libgnunet_plugin_block_set_test_done (void *cls) +libgnunet_plugin_block_seti_test_done (void *cls) { struct GNUNET_BLOCK_PluginFunctions *api = cls; @@ -120,4 +120,4 @@ libgnunet_plugin_block_set_test_done (void *cls) } -/* end of plugin_block_set_test.c */ +/* end of plugin_block_seti_test.c */ diff --git a/src/seti/seti_api.c b/src/seti/seti_api.c index d80a60684..337b7b219 100644 --- a/src/seti/seti_api.c +++ b/src/seti/seti_api.c @@ -411,7 +411,7 @@ GNUNET_SETI_create (const struct GNUNET_CONFIGURATION_Handle *cfg) set->cfg = cfg; set->mq = GNUNET_CLIENT_connect (cfg, - "set", + "seti", mq_handlers, &handle_client_set_error, set); @@ -549,7 +549,7 @@ GNUNET_SETI_prepare (const struct GNUNET_PeerIdentity *other_peer, switch (opt->type) { case GNUNET_SETI_OPTION_RETURN_INTERSECTION: - msg->return_intersection = GNUNET_YES; + msg->return_intersection = htonl (GNUNET_YES); break; default: LOG (GNUNET_ERROR_TYPE_ERROR, @@ -687,7 +687,7 @@ listen_connect (void *cls) lh->reconnect_task = NULL; GNUNET_assert (NULL == lh->mq); lh->mq = GNUNET_CLIENT_connect (lh->cfg, - "set", + "seti", mq_handlers, &handle_client_listener_error, lh); @@ -806,7 +806,7 @@ GNUNET_SETI_accept (struct GNUNET_SETI_Request *request, switch (opt->type) { case GNUNET_SETI_OPTION_RETURN_INTERSECTION: - oh->return_intersection = GNUNET_YES; + oh->return_intersection = htonl (GNUNET_YES); break; default: LOG (GNUNET_ERROR_TYPE_ERROR, diff --git a/src/seti/test_seti.conf b/src/seti/test_seti.conf index 21fe984f8..c87433419 100644 --- a/src/seti/test_seti.conf +++ b/src/seti/test_seti.conf @@ -3,7 +3,7 @@ [PATHS] GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-set/ -[set] +[seti] START_ON_DEMAND = YES #PREFIX = valgrind --leak-check=full #PREFIX = gdbserver :1234 @@ -30,4 +30,3 @@ DISABLEV6 = NO # Do we use addresses from localhost address ranges? (::1, 127.0.0.0/8) RETURN_LOCAL_ADDRESSES = YES - diff --git a/src/setu/plugin_block_setu_test.c b/src/setu/plugin_block_setu_test.c index 1de086092..fd0c8a680 100644 --- a/src/setu/plugin_block_setu_test.c +++ b/src/setu/plugin_block_setu_test.c @@ -19,7 +19,7 @@ */ /** - * @file set/plugin_block_set_test.c + * @file setu/plugin_block_setu_test.c * @brief set test block, recognizes elements with non-zero first byte as invalid * @author Christian Grothoff */ @@ -46,16 +46,16 @@ * @return characterization of result */ static enum GNUNET_BLOCK_EvaluationResult -block_plugin_set_test_evaluate (void *cls, - struct GNUNET_BLOCK_Context *ctx, - enum GNUNET_BLOCK_Type type, - struct GNUNET_BLOCK_Group *group, - enum GNUNET_BLOCK_EvaluationOptions eo, - const struct GNUNET_HashCode *query, - const void *xquery, - size_t xquery_size, - const void *reply_block, - size_t reply_block_size) +block_plugin_setu_test_evaluate (void *cls, + struct GNUNET_BLOCK_Context *ctx, + enum GNUNET_BLOCK_Type type, + struct GNUNET_BLOCK_Group *group, + enum GNUNET_BLOCK_EvaluationOptions eo, + const struct GNUNET_HashCode *query, + const void *xquery, + size_t xquery_size, + const void *reply_block, + size_t reply_block_size) { if ((NULL == reply_block) || (reply_block_size == 0) || @@ -77,11 +77,11 @@ block_plugin_set_test_evaluate (void *cls, * (or if extracting a key from a block of this type does not work) */ static int -block_plugin_set_test_get_key (void *cls, - enum GNUNET_BLOCK_Type type, - const void *block, - size_t block_size, - struct GNUNET_HashCode *key) +block_plugin_setu_test_get_key (void *cls, + enum GNUNET_BLOCK_Type type, + const void *block, + size_t block_size, + struct GNUNET_HashCode *key) { return GNUNET_SYSERR; } @@ -91,17 +91,17 @@ block_plugin_set_test_get_key (void *cls, * Entry point for the plugin. */ void * -libgnunet_plugin_block_set_test_init (void *cls) +libgnunet_plugin_block_setu_test_init (void *cls) { static enum GNUNET_BLOCK_Type types[] = { - GNUNET_BLOCK_TYPE_SET_TEST, + GNUNET_BLOCK_TYPE_SETU_TEST, GNUNET_BLOCK_TYPE_ANY /* end of list */ }; struct GNUNET_BLOCK_PluginFunctions *api; api = GNUNET_new (struct GNUNET_BLOCK_PluginFunctions); - api->evaluate = &block_plugin_set_test_evaluate; - api->get_key = &block_plugin_set_test_get_key; + api->evaluate = &block_plugin_setu_test_evaluate; + api->get_key = &block_plugin_setu_test_get_key; api->types = types; return api; } @@ -111,7 +111,7 @@ libgnunet_plugin_block_set_test_init (void *cls) * Exit point from the plugin. */ void * -libgnunet_plugin_block_set_test_done (void *cls) +libgnunet_plugin_block_setu_test_done (void *cls) { struct GNUNET_BLOCK_PluginFunctions *api = cls; @@ -120,4 +120,4 @@ libgnunet_plugin_block_set_test_done (void *cls) } -/* end of plugin_block_set_test.c */ +/* end of plugin_block_setu_test.c */ -- cgit v1.2.3 From 5660868ce7523c4bda8e25954fda0ae2d28a9702 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 19 Aug 2020 10:55:36 +0200 Subject: fix seti testcase --- src/seti/gnunet-service-seti.c | 6 ++++++ src/seti/seti_api.c | 3 ++- src/seti/test_seti_api.c | 12 ++++++++---- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/seti/gnunet-service-seti.c b/src/seti/gnunet-service-seti.c index 618d53128..af478233b 100644 --- a/src/seti/gnunet-service-seti.c +++ b/src/seti/gnunet-service-seti.c @@ -2200,6 +2200,9 @@ handle_client_evaluate (void *cls, UINT32_MAX); op->peer = msg->target_peer; op->return_intersection = htonl (msg->return_intersection); + fprintf (stderr, + "Return intersection for evaluate is %d\n", + op->return_intersection); op->client_request_id = ntohl (msg->request_id); context = GNUNET_MQ_extract_nested_mh (msg); @@ -2364,6 +2367,9 @@ handle_client_accept (void *cls, listener = op->listener; op->listener = NULL; op->return_intersection = htonl (msg->return_intersection); + fprintf (stderr, + "Return intersection for accept is %d\n", + op->return_intersection); GNUNET_CONTAINER_DLL_remove (listener->op_head, listener->op_tail, op); diff --git a/src/seti/seti_api.c b/src/seti/seti_api.c index 337b7b219..5b88b0469 100644 --- a/src/seti/seti_api.c +++ b/src/seti/seti_api.c @@ -806,7 +806,8 @@ GNUNET_SETI_accept (struct GNUNET_SETI_Request *request, switch (opt->type) { case GNUNET_SETI_OPTION_RETURN_INTERSECTION: - oh->return_intersection = htonl (GNUNET_YES); + oh->return_intersection = GNUNET_YES; + msg->return_intersection = htonl (GNUNET_YES); break; default: LOG (GNUNET_ERROR_TYPE_ERROR, diff --git a/src/seti/test_seti_api.c b/src/seti/test_seti_api.c index 22a3a06e5..9074fab41 100644 --- a/src/seti/test_seti_api.c +++ b/src/seti/test_seti_api.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2012-2014 GNUnet e.V. + Copyright (C) 2012-2014, 2020 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published @@ -108,14 +108,18 @@ result_cb_set2 (void *cls, break; case GNUNET_SETI_STATUS_DONE: oh2 = NULL; - GNUNET_assert (1 == count); + GNUNET_break (1 == count); + if (1 != count) + ret |= 2; GNUNET_SETI_destroy (set2); set2 = NULL; if (NULL == set1) GNUNET_SCHEDULER_shutdown (); break; - default: - GNUNET_assert (0); + case GNUNET_SETI_STATUS_DEL_LOCAL: + /* unexpected! */ + ret = 1; + break; } } -- cgit v1.2.3 From 2d010ea152050af11acdb3d233f80c443dfd55c8 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 19 Aug 2020 10:59:27 +0200 Subject: change revocation logic to use setu service in lieu of deprecated set service --- src/revocation/Makefile.am | 2 +- src/revocation/gnunet-service-revocation.c | 90 +++++++++++++----------------- 2 files changed, 41 insertions(+), 51 deletions(-) diff --git a/src/revocation/Makefile.am b/src/revocation/Makefile.am index a90f8cd79..9d98502a6 100644 --- a/src/revocation/Makefile.am +++ b/src/revocation/Makefile.am @@ -78,7 +78,7 @@ gnunet_service_revocation_SOURCES = \ gnunet_service_revocation_LDADD = \ libgnunetrevocation.la \ $(top_builddir)/src/core/libgnunetcore.la \ - $(top_builddir)/src/set/libgnunetset.la \ + $(top_builddir)/src/setu/libgnunetsetu.la \ $(top_builddir)/src/statistics/libgnunetstatistics.la \ $(top_builddir)/src/util/libgnunetutil.la \ -lm \ diff --git a/src/revocation/gnunet-service-revocation.c b/src/revocation/gnunet-service-revocation.c index 81a30748c..ddebb38ad 100644 --- a/src/revocation/gnunet-service-revocation.c +++ b/src/revocation/gnunet-service-revocation.c @@ -45,7 +45,7 @@ #include "gnunet_statistics_service.h" #include "gnunet_core_service.h" #include "gnunet_revocation_service.h" -#include "gnunet_set_service.h" +#include "gnunet_setu_service.h" #include "revocation.h" #include @@ -73,14 +73,14 @@ struct PeerEntry /** * Handle to active set union operation (over revocation sets). */ - struct GNUNET_SET_OperationHandle *so; + struct GNUNET_SETU_OperationHandle *so; }; /** * Set from all revocations known to us. */ -static struct GNUNET_SET_Handle *revocation_set; +static struct GNUNET_SETU_Handle *revocation_set; /** * Hash map with all revoked keys, maps the hash of the public key @@ -121,7 +121,7 @@ static struct GNUNET_DISK_FileHandle *revocation_db; /** * Handle for us listening to incoming revocation set union requests. */ -static struct GNUNET_SET_ListenHandle *revocation_union_listen_handle; +static struct GNUNET_SETU_ListenHandle *revocation_union_listen_handle; /** * Amount of work required (W-bit collisions) for REVOCATION proofs, in collision-bits. @@ -302,7 +302,7 @@ publicize_rm (const struct RevokeMessage *rm) { struct RevokeMessage *cp; struct GNUNET_HashCode hc; - struct GNUNET_SET_Element e; + struct GNUNET_SETU_Element e; GNUNET_CRYPTO_hash (&rm->proof_of_work.key, sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey), @@ -350,10 +350,10 @@ publicize_rm (const struct RevokeMessage *rm) e.element_type = GNUNET_BLOCK_TYPE_REVOCATION; e.data = rm; if (GNUNET_OK != - GNUNET_SET_add_element (revocation_set, - &e, - NULL, - NULL)) + GNUNET_SETU_add_element (revocation_set, + &e, + NULL, + NULL)) { GNUNET_break (0); return GNUNET_OK; @@ -426,22 +426,22 @@ handle_p2p_revoke (void *cls, * validate and then add to our revocation list (and set). * * @param cls closure - * @param element a result element, only valid if status is #GNUNET_SET_STATUS_OK + * @param element a result element, only valid if status is #GNUNET_SETU_STATUS_OK * @param current_size current set size - * @param status see `enum GNUNET_SET_Status` + * @param status see `enum GNUNET_SETU_Status` */ static void add_revocation (void *cls, - const struct GNUNET_SET_Element *element, + const struct GNUNET_SETU_Element *element, uint64_t current_size, - enum GNUNET_SET_Status status) + enum GNUNET_SETU_Status status) { struct PeerEntry *peer_entry = cls; const struct RevokeMessage *rm; switch (status) { - case GNUNET_SET_STATUS_OK: + case GNUNET_SETU_STATUS_ADD_LOCAL: if (element->size != sizeof(struct RevokeMessage)) { GNUNET_break_op (0); @@ -464,8 +464,7 @@ add_revocation (void *cls, "# revocation messages received via set union"), 1, GNUNET_NO); break; - - case GNUNET_SET_STATUS_FAILURE: + case GNUNET_SETU_STATUS_FAILURE: GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("Error computing revocation set union with %s\n"), GNUNET_i2s (&peer_entry->id)); @@ -475,11 +474,7 @@ add_revocation (void *cls, 1, GNUNET_NO); break; - - case GNUNET_SET_STATUS_HALF_DONE: - break; - - case GNUNET_SET_STATUS_DONE: + case GNUNET_SETU_STATUS_DONE: peer_entry->so = NULL; GNUNET_STATISTICS_update (stats, gettext_noop ( @@ -487,7 +482,6 @@ add_revocation (void *cls, 1, GNUNET_NO); break; - default: GNUNET_break (0); break; @@ -511,16 +505,15 @@ transmit_task_cb (void *cls) GNUNET_i2s (&peer_entry->id)); peer_entry->transmit_task = NULL; GNUNET_assert (NULL == peer_entry->so); - peer_entry->so = GNUNET_SET_prepare (&peer_entry->id, - &revocation_set_union_app_id, - NULL, - GNUNET_SET_RESULT_ADDED, - (struct GNUNET_SET_Option[]) { { 0 } }, - &add_revocation, - peer_entry); + peer_entry->so = GNUNET_SETU_prepare (&peer_entry->id, + &revocation_set_union_app_id, + NULL, + (struct GNUNET_SETU_Option[]) { { 0 } }, + &add_revocation, + peer_entry); if (GNUNET_OK != - GNUNET_SET_commit (peer_entry->so, - revocation_set)) + GNUNET_SETU_commit (peer_entry->so, + revocation_set)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _ ("SET service crashed, terminating revocation service\n")); @@ -626,7 +619,7 @@ handle_core_disconnect (void *cls, } if (NULL != peer_entry->so) { - GNUNET_SET_operation_cancel (peer_entry->so); + GNUNET_SETU_operation_cancel (peer_entry->so); peer_entry->so = NULL; } GNUNET_free (peer_entry); @@ -665,12 +658,12 @@ shutdown_task (void *cls) { if (NULL != revocation_set) { - GNUNET_SET_destroy (revocation_set); + GNUNET_SETU_destroy (revocation_set); revocation_set = NULL; } if (NULL != revocation_union_listen_handle) { - GNUNET_SET_listen_cancel (revocation_union_listen_handle); + GNUNET_SETU_listen_cancel (revocation_union_listen_handle); revocation_union_listen_handle = NULL; } if (NULL != core_api) @@ -729,7 +722,7 @@ core_init (void *cls, * @param other_peer the other peer * @param context_msg message with application specific information from * the other peer - * @param request request from the other peer (never NULL), use GNUNET_SET_accept() + * @param request request from the other peer (never NULL), use GNUNET_SETU_accept() * to accept it, otherwise the request will be refused * Note that we can't just return value from the listen callback, * as it is also necessary to specify the set we want to do the @@ -740,7 +733,7 @@ static void handle_revocation_union_request (void *cls, const struct GNUNET_PeerIdentity *other_peer, const struct GNUNET_MessageHeader *context_msg, - struct GNUNET_SET_Request *request) + struct GNUNET_SETU_Request *request) { struct PeerEntry *peer_entry; @@ -763,14 +756,13 @@ handle_revocation_union_request (void *cls, GNUNET_break_op (0); return; } - peer_entry->so = GNUNET_SET_accept (request, - GNUNET_SET_RESULT_ADDED, - (struct GNUNET_SET_Option[]) { { 0 } }, - &add_revocation, - peer_entry); + peer_entry->so = GNUNET_SETU_accept (request, + (struct GNUNET_SETU_Option[]) { { 0 } }, + &add_revocation, + peer_entry); if (GNUNET_OK != - GNUNET_SET_commit (peer_entry->so, - revocation_set)) + GNUNET_SETU_commit (peer_entry->so, + revocation_set)) { GNUNET_break (0); GNUNET_SCHEDULER_shutdown (); @@ -858,14 +850,12 @@ run (void *cls, return; } - revocation_set = GNUNET_SET_create (cfg, - GNUNET_SET_OPERATION_UNION); + revocation_set = GNUNET_SETU_create (cfg); revocation_union_listen_handle - = GNUNET_SET_listen (cfg, - GNUNET_SET_OPERATION_UNION, - &revocation_set_union_app_id, - &handle_revocation_union_request, - NULL); + = GNUNET_SETU_listen (cfg, + &revocation_set_union_app_id, + &handle_revocation_union_request, + NULL); revocation_db = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READWRITE | GNUNET_DISK_OPEN_CREATE, -- cgit v1.2.3 From 6dabecce0f319d5e6c1a26dc09abf689c7249971 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 19 Aug 2020 11:05:13 +0200 Subject: change scalarproduct to use seti service in lieu of deprecated set service --- src/scalarproduct/Makefile.am | 8 +-- .../gnunet-service-scalarproduct-ecc_alice.c | 75 ++++++++++----------- .../gnunet-service-scalarproduct-ecc_bob.c | 70 +++++++++----------- .../gnunet-service-scalarproduct_alice.c | 77 ++++++++++------------ .../gnunet-service-scalarproduct_bob.c | 71 +++++++++----------- 5 files changed, 133 insertions(+), 168 deletions(-) diff --git a/src/scalarproduct/Makefile.am b/src/scalarproduct/Makefile.am index 311cfd1af..f3448725a 100644 --- a/src/scalarproduct/Makefile.am +++ b/src/scalarproduct/Makefile.am @@ -39,7 +39,7 @@ gnunet_service_scalarproduct_alice_SOURCES = \ gnunet_service_scalarproduct_alice_LDADD = \ $(top_builddir)/src/util/libgnunetutil.la \ $(top_builddir)/src/cadet/libgnunetcadet.la \ - $(top_builddir)/src/set/libgnunetset.la \ + $(top_builddir)/src/seti/libgnunetseti.la \ $(LIBGCRYPT_LIBS) \ -lgcrypt \ $(GN_LIBINTL) @@ -50,7 +50,7 @@ gnunet_service_scalarproduct_bob_SOURCES = \ gnunet_service_scalarproduct_bob_LDADD = \ $(top_builddir)/src/util/libgnunetutil.la \ $(top_builddir)/src/cadet/libgnunetcadet.la \ - $(top_builddir)/src/set/libgnunetset.la \ + $(top_builddir)/src/seti/libgnunetseti.la \ $(LIBGCRYPT_LIBS) \ -lgcrypt \ $(GN_LIBINTL) @@ -61,7 +61,7 @@ gnunet_service_scalarproduct_ecc_alice_SOURCES = \ gnunet_service_scalarproduct_ecc_alice_LDADD = \ $(top_builddir)/src/util/libgnunetutil.la \ $(top_builddir)/src/cadet/libgnunetcadet.la \ - $(top_builddir)/src/set/libgnunetset.la \ + $(top_builddir)/src/seti/libgnunetseti.la \ $(LIBGCRYPT_LIBS) \ -lgcrypt \ $(GN_LIBINTL) @@ -72,7 +72,7 @@ gnunet_service_scalarproduct_ecc_bob_SOURCES = \ gnunet_service_scalarproduct_ecc_bob_LDADD = \ $(top_builddir)/src/util/libgnunetutil.la \ $(top_builddir)/src/cadet/libgnunetcadet.la \ - $(top_builddir)/src/set/libgnunetset.la \ + $(top_builddir)/src/seti/libgnunetseti.la \ $(LIBGCRYPT_LIBS) \ -lgcrypt \ $(GN_LIBINTL) diff --git a/src/scalarproduct/gnunet-service-scalarproduct-ecc_alice.c b/src/scalarproduct/gnunet-service-scalarproduct-ecc_alice.c index 20ab292cf..447451aef 100644 --- a/src/scalarproduct/gnunet-service-scalarproduct-ecc_alice.c +++ b/src/scalarproduct/gnunet-service-scalarproduct-ecc_alice.c @@ -32,7 +32,7 @@ #include "gnunet_applications.h" #include "gnunet_protocols.h" #include "gnunet_scalarproduct_service.h" -#include "gnunet_set_service.h" +#include "gnunet_seti_service.h" #include "scalarproduct.h" #include "gnunet-service-scalarproduct-ecc.h" @@ -114,18 +114,18 @@ struct AliceServiceSession * Set of elements for which will conduction an intersection. * the resulting elements are then used for computing the scalar product. */ - struct GNUNET_SET_Handle *intersection_set; + struct GNUNET_SETI_Handle *intersection_set; /** * Set of elements for which will conduction an intersection. * the resulting elements are then used for computing the scalar product. */ - struct GNUNET_SET_OperationHandle *intersection_op; + struct GNUNET_SETI_OperationHandle *intersection_op; /** * Handle to Alice's Intersection operation listening for Bob */ - struct GNUNET_SET_ListenHandle *intersection_listen; + struct GNUNET_SETI_ListenHandle *intersection_listen; /** * channel-handle associated with our cadet handle @@ -256,18 +256,18 @@ destroy_service_session (struct AliceServiceSession *s) } if (NULL != s->intersection_listen) { - GNUNET_SET_listen_cancel (s->intersection_listen); + GNUNET_SETI_listen_cancel (s->intersection_listen); s->intersection_listen = NULL; } if (NULL != s->intersection_op) { LOG (GNUNET_ERROR_TYPE_DEBUG, "Set intersection, op still ongoing!\n"); - GNUNET_SET_operation_cancel (s->intersection_op); + GNUNET_SETI_operation_cancel (s->intersection_op); s->intersection_op = NULL; } if (NULL != s->intersection_set) { - GNUNET_SET_destroy (s->intersection_set); + GNUNET_SETI_destroy (s->intersection_set); s->intersection_set = NULL; } if (NULL != s->sorted_elements) @@ -649,22 +649,22 @@ send_alices_cryptodata_message (struct AliceServiceSession *s) * to indicate that the set intersection operation is done. * * @param cls closure with the `struct AliceServiceSession` - * @param element a result element, only valid if status is #GNUNET_SET_STATUS_OK + * @param element a result element, only valid if status is #GNUNET_SETI_STATUS_OK * @param current_size current set size * @param status what has happened with the set intersection? */ static void cb_intersection_element_removed (void *cls, - const struct GNUNET_SET_Element *element, + const struct GNUNET_SETI_Element *element, uint64_t current_size, - enum GNUNET_SET_Status status) + enum GNUNET_SETI_Status status) { struct AliceServiceSession *s = cls; struct GNUNET_SCALARPRODUCT_Element *se; switch (status) { - case GNUNET_SET_STATUS_OK: + case GNUNET_SETI_STATUS_DEL_LOCAL: /* this element has been removed from the set */ se = GNUNET_CONTAINER_multihashmap_get (s->intersected_elements, element->data); @@ -680,34 +680,27 @@ cb_intersection_element_removed (void *cls, se)); GNUNET_free (se); return; - - case GNUNET_SET_STATUS_DONE: + case GNUNET_SETI_STATUS_DONE: s->intersection_op = NULL; if (NULL != s->intersection_set) { - GNUNET_SET_destroy (s->intersection_set); + GNUNET_SETI_destroy (s->intersection_set); s->intersection_set = NULL; } send_alices_cryptodata_message (s); return; - - case GNUNET_SET_STATUS_HALF_DONE: - /* unexpected for intersection */ - GNUNET_break (0); - return; - - case GNUNET_SET_STATUS_FAILURE: + case GNUNET_SETI_STATUS_FAILURE: /* unhandled status code */ LOG (GNUNET_ERROR_TYPE_DEBUG, "Set intersection failed!\n"); if (NULL != s->intersection_listen) { - GNUNET_SET_listen_cancel (s->intersection_listen); + GNUNET_SETI_listen_cancel (s->intersection_listen); s->intersection_listen = NULL; } s->intersection_op = NULL; if (NULL != s->intersection_set) { - GNUNET_SET_destroy (s->intersection_set); + GNUNET_SETI_destroy (s->intersection_set); s->intersection_set = NULL; } s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE; @@ -729,7 +722,7 @@ cb_intersection_element_removed (void *cls, * @param other_peer the other peer * @param context_msg message with application specific information from * the other peer - * @param request request from the other peer (never NULL), use GNUNET_SET_accept() + * @param request request from the other peer (never NULL), use GNUNET_SETI_accept() * to accept it, otherwise the request will be refused * Note that we can't just return value from the listen callback, * as it is also necessary to specify the set we want to do the @@ -740,7 +733,7 @@ static void cb_intersection_request_alice (void *cls, const struct GNUNET_PeerIdentity *other_peer, const struct GNUNET_MessageHeader *context_msg, - struct GNUNET_SET_Request *request) + struct GNUNET_SETI_Request *request) { struct AliceServiceSession *s = cls; @@ -752,11 +745,11 @@ cb_intersection_request_alice (void *cls, GNUNET_break_op (0); return; } - s->intersection_op = GNUNET_SET_accept (request, - GNUNET_SET_RESULT_REMOVED, - (struct GNUNET_SET_Option[]){ { 0 } }, - &cb_intersection_element_removed, - s); + s->intersection_op = GNUNET_SETI_accept (request, + (struct + GNUNET_SETI_Option[]){ { 0 } }, + &cb_intersection_element_removed, + s); if (NULL == s->intersection_op) { GNUNET_break (0); @@ -764,7 +757,7 @@ cb_intersection_request_alice (void *cls, prepare_client_end_notification (s); return; } - if (GNUNET_OK != GNUNET_SET_commit (s->intersection_op, s->intersection_set)) + if (GNUNET_OK != GNUNET_SETI_commit (s->intersection_op, s->intersection_set)) { GNUNET_break (0); s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE; @@ -812,11 +805,10 @@ client_request_complete_alice (struct AliceServiceSession *s) return; } s->cadet_mq = GNUNET_CADET_get_mq (s->channel); - s->intersection_listen = GNUNET_SET_listen (cfg, - GNUNET_SET_OPERATION_INTERSECTION, - &set_sid, - &cb_intersection_request_alice, - s); + s->intersection_listen = GNUNET_SETI_listen (cfg, + &set_sid, + &cb_intersection_request_alice, + s); if (NULL == s->intersection_listen) { s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE; @@ -883,7 +875,7 @@ handle_alice_client_message_multipart ( struct AliceServiceSession *s = cls; uint32_t contained_count; const struct GNUNET_SCALARPRODUCT_Element *elements; - struct GNUNET_SET_Element set_elem; + struct GNUNET_SETI_Element set_elem; struct GNUNET_SCALARPRODUCT_Element *elem; contained_count = ntohl (msg->element_count_contained); @@ -908,7 +900,7 @@ handle_alice_client_message_multipart ( set_elem.data = &elem->key; set_elem.size = sizeof(elem->key); set_elem.element_type = 0; - GNUNET_SET_add_element (s->intersection_set, &set_elem, NULL, NULL); + GNUNET_SETI_add_element (s->intersection_set, &set_elem, NULL, NULL); s->used_element_count++; } GNUNET_SERVICE_client_continue (s->client); @@ -978,7 +970,7 @@ handle_alice_client_message (void *cls, uint32_t contained_count; uint32_t total_count; const struct GNUNET_SCALARPRODUCT_Element *elements; - struct GNUNET_SET_Element set_elem; + struct GNUNET_SETI_Element set_elem; struct GNUNET_SCALARPRODUCT_Element *elem; total_count = ntohl (msg->element_count_total); @@ -991,8 +983,7 @@ handle_alice_client_message (void *cls, elements = (const struct GNUNET_SCALARPRODUCT_Element *) &msg[1]; s->intersected_elements = GNUNET_CONTAINER_multihashmap_create (s->total, GNUNET_YES); - s->intersection_set = - GNUNET_SET_create (cfg, GNUNET_SET_OPERATION_INTERSECTION); + s->intersection_set = GNUNET_SETI_create (cfg); for (uint32_t i = 0; i < contained_count; i++) { if (0 == GNUNET_ntohll (elements[i].value)) @@ -1015,7 +1006,7 @@ handle_alice_client_message (void *cls, set_elem.data = &elem->key; set_elem.size = sizeof(elem->key); set_elem.element_type = 0; - GNUNET_SET_add_element (s->intersection_set, &set_elem, NULL, NULL); + GNUNET_SETI_add_element (s->intersection_set, &set_elem, NULL, NULL); s->used_element_count++; } GNUNET_SERVICE_client_continue (s->client); diff --git a/src/scalarproduct/gnunet-service-scalarproduct-ecc_bob.c b/src/scalarproduct/gnunet-service-scalarproduct-ecc_bob.c index 2da79f845..4c835d52a 100644 --- a/src/scalarproduct/gnunet-service-scalarproduct-ecc_bob.c +++ b/src/scalarproduct/gnunet-service-scalarproduct-ecc_bob.c @@ -32,7 +32,7 @@ #include "gnunet_applications.h" #include "gnunet_protocols.h" #include "gnunet_scalarproduct_service.h" -#include "gnunet_set_service.h" +#include "gnunet_seti_service.h" #include "scalarproduct.h" #include "gnunet-service-scalarproduct-ecc.h" @@ -83,13 +83,13 @@ struct BobServiceSession * Set of elements for which we will be conducting an intersection. * The resulting elements are then used for computing the scalar product. */ - struct GNUNET_SET_Handle *intersection_set; + struct GNUNET_SETI_Handle *intersection_set; /** * Set of elements for which will conduction an intersection. * the resulting elements are then used for computing the scalar product. */ - struct GNUNET_SET_OperationHandle *intersection_op; + struct GNUNET_SETI_OperationHandle *intersection_op; /** * Our open port. @@ -235,12 +235,12 @@ destroy_service_session (struct BobServiceSession *s) } if (NULL != s->intersection_op) { - GNUNET_SET_operation_cancel (s->intersection_op); + GNUNET_SETI_operation_cancel (s->intersection_op); s->intersection_op = NULL; } if (NULL != s->intersection_set) { - GNUNET_SET_destroy (s->intersection_set); + GNUNET_SETI_destroy (s->intersection_set); s->intersection_set = NULL; } if (NULL != s->sorted_elements) @@ -578,22 +578,22 @@ handle_alices_cryptodata_message (void *cls, * that needs to be removed from the result set. * * @param cls closure with the `struct BobServiceSession` - * @param element a result element, only valid if status is #GNUNET_SET_STATUS_OK + * @param element a result element, only valid if status is #GNUNET_SETI_STATUS_OK * @param current_size current set size * @param status what has happened with the set intersection? */ static void cb_intersection_element_removed (void *cls, - const struct GNUNET_SET_Element *element, + const struct GNUNET_SETI_Element *element, uint64_t current_size, - enum GNUNET_SET_Status status) + enum GNUNET_SETI_Status status) { struct BobServiceSession *s = cls; struct GNUNET_SCALARPRODUCT_Element *se; switch (status) { - case GNUNET_SET_STATUS_OK: + case GNUNET_SETI_STATUS_DEL_LOCAL: /* this element has been removed from the set */ se = GNUNET_CONTAINER_multihashmap_get (s->intersected_elements, element->data); @@ -609,8 +609,7 @@ cb_intersection_element_removed (void *cls, se)); GNUNET_free (se); return; - - case GNUNET_SET_STATUS_DONE: + case GNUNET_SETI_STATUS_DONE: s->intersection_op = NULL; GNUNET_break (NULL == s->intersection_set); GNUNET_CADET_receive_done (s->channel); @@ -625,20 +624,14 @@ cb_intersection_element_removed (void *cls, transmit_bobs_cryptodata_message (s); } return; - - case GNUNET_SET_STATUS_HALF_DONE: - /* unexpected for intersection */ - GNUNET_break (0); - return; - - case GNUNET_SET_STATUS_FAILURE: + case GNUNET_SETI_STATUS_FAILURE: /* unhandled status code */ LOG (GNUNET_ERROR_TYPE_DEBUG, "Set intersection failed!\n"); s->intersection_op = NULL; if (NULL != s->intersection_set) { - GNUNET_SET_destroy (s->intersection_set); + GNUNET_SETI_destroy (s->intersection_set); s->intersection_set = NULL; } s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE; @@ -672,23 +665,22 @@ start_intersection (struct BobServiceSession *s) (unsigned int) s->total); s->intersection_op - = GNUNET_SET_prepare (&s->peer, - &set_sid, - NULL, - GNUNET_SET_RESULT_REMOVED, - (struct GNUNET_SET_Option[]) { { 0 } }, - &cb_intersection_element_removed, - s); + = GNUNET_SETI_prepare (&s->peer, + &set_sid, + NULL, + (struct GNUNET_SETI_Option[]) { { 0 } }, + &cb_intersection_element_removed, + s); if (GNUNET_OK != - GNUNET_SET_commit (s->intersection_op, - s->intersection_set)) + GNUNET_SETI_commit (s->intersection_op, + s->intersection_set)) { GNUNET_break (0); s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE; prepare_client_end_notification (s); return; } - GNUNET_SET_destroy (s->intersection_set); + GNUNET_SETI_destroy (s->intersection_set); s->intersection_set = NULL; } @@ -797,7 +789,7 @@ handle_bob_client_message_multipart (void *cls, struct BobServiceSession *s = cls; uint32_t contained_count; const struct GNUNET_SCALARPRODUCT_Element *elements; - struct GNUNET_SET_Element set_elem; + struct GNUNET_SETI_Element set_elem; struct GNUNET_SCALARPRODUCT_Element *elem; contained_count = ntohl (msg->element_count_contained); @@ -821,9 +813,9 @@ handle_bob_client_message_multipart (void *cls, set_elem.data = &elem->key; set_elem.size = sizeof(elem->key); set_elem.element_type = 0; - GNUNET_SET_add_element (s->intersection_set, - &set_elem, - NULL, NULL); + GNUNET_SETI_add_element (s->intersection_set, + &set_elem, + NULL, NULL); } s->client_received_element_count += contained_count; GNUNET_SERVICE_client_continue (s->client); @@ -913,7 +905,7 @@ handle_bob_client_message (void *cls, uint32_t contained_count; uint32_t total_count; const struct GNUNET_SCALARPRODUCT_Element *elements; - struct GNUNET_SET_Element set_elem; + struct GNUNET_SETI_Element set_elem; struct GNUNET_SCALARPRODUCT_Element *elem; total_count = ntohl (msg->element_count_total); @@ -927,9 +919,7 @@ handle_bob_client_message (void *cls, s->intersected_elements = GNUNET_CONTAINER_multihashmap_create (s->total, GNUNET_YES); - s->intersection_set - = GNUNET_SET_create (cfg, - GNUNET_SET_OPERATION_INTERSECTION); + s->intersection_set = GNUNET_SETI_create (cfg); for (uint32_t i = 0; i < contained_count; i++) { if (0 == GNUNET_ntohll (elements[i].value)) @@ -951,9 +941,9 @@ handle_bob_client_message (void *cls, set_elem.data = &elem->key; set_elem.size = sizeof(elem->key); set_elem.element_type = 0; - GNUNET_SET_add_element (s->intersection_set, - &set_elem, - NULL, NULL); + GNUNET_SETI_add_element (s->intersection_set, + &set_elem, + NULL, NULL); s->used_element_count++; } GNUNET_SERVICE_client_continue (s->client); diff --git a/src/scalarproduct/gnunet-service-scalarproduct_alice.c b/src/scalarproduct/gnunet-service-scalarproduct_alice.c index 44534c850..1ca7f61da 100644 --- a/src/scalarproduct/gnunet-service-scalarproduct_alice.c +++ b/src/scalarproduct/gnunet-service-scalarproduct_alice.c @@ -32,7 +32,7 @@ #include "gnunet_applications.h" #include "gnunet_protocols.h" #include "gnunet_scalarproduct_service.h" -#include "gnunet_set_service.h" +#include "gnunet_seti_service.h" #include "scalarproduct.h" #include "gnunet-service-scalarproduct.h" @@ -99,18 +99,18 @@ struct AliceServiceSession * Set of elements for which will conduction an intersection. * the resulting elements are then used for computing the scalar product. */ - struct GNUNET_SET_Handle *intersection_set; + struct GNUNET_SETI_Handle *intersection_set; /** * Set of elements for which will conduction an intersection. * the resulting elements are then used for computing the scalar product. */ - struct GNUNET_SET_OperationHandle *intersection_op; + struct GNUNET_SETI_OperationHandle *intersection_op; /** * Handle to Alice's Intersection operation listening for Bob */ - struct GNUNET_SET_ListenHandle *intersection_listen; + struct GNUNET_SETI_ListenHandle *intersection_listen; /** * channel-handle associated with our cadet handle @@ -265,17 +265,17 @@ destroy_service_session (struct AliceServiceSession *s) } if (NULL != s->intersection_listen) { - GNUNET_SET_listen_cancel (s->intersection_listen); + GNUNET_SETI_listen_cancel (s->intersection_listen); s->intersection_listen = NULL; } if (NULL != s->intersection_op) { - GNUNET_SET_operation_cancel (s->intersection_op); + GNUNET_SETI_operation_cancel (s->intersection_op); s->intersection_op = NULL; } if (NULL != s->intersection_set) { - GNUNET_SET_destroy (s->intersection_set); + GNUNET_SETI_destroy (s->intersection_set); s->intersection_set = NULL; } if (NULL != s->sorted_elements) @@ -894,22 +894,22 @@ send_alices_cryptodata_message (struct AliceServiceSession *s) * to indicate that the set intersection operation is done. * * @param cls closure with the `struct AliceServiceSession` - * @param element a result element, only valid if status is #GNUNET_SET_STATUS_OK + * @param element a result element, only valid if status is #GNUNET_SETI_STATUS_OK * @param current_size current set size * @param status what has happened with the set intersection? */ static void cb_intersection_element_removed (void *cls, - const struct GNUNET_SET_Element *element, + const struct GNUNET_SETI_Element *element, uint64_t current_size, - enum GNUNET_SET_Status status) + enum GNUNET_SETI_Status status) { struct AliceServiceSession *s = cls; struct GNUNET_SCALARPRODUCT_Element *se; switch (status) { - case GNUNET_SET_STATUS_OK: + case GNUNET_SETI_STATUS_DEL_LOCAL: /* this element has been removed from the set */ se = GNUNET_CONTAINER_multihashmap_get (s->intersected_elements, element->data); @@ -926,33 +926,27 @@ cb_intersection_element_removed (void *cls, GNUNET_free (se); return; - case GNUNET_SET_STATUS_DONE: + case GNUNET_SETI_STATUS_DONE: s->intersection_op = NULL; if (NULL != s->intersection_set) { - GNUNET_SET_destroy (s->intersection_set); + GNUNET_SETI_destroy (s->intersection_set); s->intersection_set = NULL; } send_alices_cryptodata_message (s); return; - - case GNUNET_SET_STATUS_HALF_DONE: - /* unexpected for intersection */ - GNUNET_break (0); - return; - - case GNUNET_SET_STATUS_FAILURE: + case GNUNET_SETI_STATUS_FAILURE: /* unhandled status code */ LOG (GNUNET_ERROR_TYPE_DEBUG, "Set intersection failed!\n"); if (NULL != s->intersection_listen) { - GNUNET_SET_listen_cancel (s->intersection_listen); + GNUNET_SETI_listen_cancel (s->intersection_listen); s->intersection_listen = NULL; } s->intersection_op = NULL; if (NULL != s->intersection_set) { - GNUNET_SET_destroy (s->intersection_set); + GNUNET_SETI_destroy (s->intersection_set); s->intersection_set = NULL; } s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE; @@ -974,7 +968,7 @@ cb_intersection_element_removed (void *cls, * @param other_peer the other peer * @param context_msg message with application specific information from * the other peer - * @param request request from the other peer (never NULL), use GNUNET_SET_accept() + * @param request request from the other peer (never NULL), use GNUNET_SETI_accept() * to accept it, otherwise the request will be refused * Note that we can't just return value from the listen callback, * as it is also necessary to specify the set we want to do the @@ -985,7 +979,7 @@ static void cb_intersection_request_alice (void *cls, const struct GNUNET_PeerIdentity *other_peer, const struct GNUNET_MessageHeader *context_msg, - struct GNUNET_SET_Request *request) + struct GNUNET_SETI_Request *request) { struct AliceServiceSession *s = cls; @@ -994,11 +988,11 @@ cb_intersection_request_alice (void *cls, GNUNET_break_op (0); return; } - s->intersection_op = GNUNET_SET_accept (request, - GNUNET_SET_RESULT_REMOVED, - (struct GNUNET_SET_Option[]){ { 0 } }, - &cb_intersection_element_removed, - s); + s->intersection_op = GNUNET_SETI_accept (request, + (struct + GNUNET_SETI_Option[]){ { 0 } }, + &cb_intersection_element_removed, + s); if (NULL == s->intersection_op) { GNUNET_break (0); @@ -1006,7 +1000,7 @@ cb_intersection_request_alice (void *cls, prepare_client_end_notification (s); return; } - if (GNUNET_OK != GNUNET_SET_commit (s->intersection_op, s->intersection_set)) + if (GNUNET_OK != GNUNET_SETI_commit (s->intersection_op, s->intersection_set)) { GNUNET_break (0); s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE; @@ -1055,11 +1049,10 @@ client_request_complete_alice (struct AliceServiceSession *s) return; } s->cadet_mq = GNUNET_CADET_get_mq (s->channel); - s->intersection_listen = GNUNET_SET_listen (cfg, - GNUNET_SET_OPERATION_INTERSECTION, - &s->session_id, - &cb_intersection_request_alice, - s); + s->intersection_listen = GNUNET_SETI_listen (cfg, + &s->session_id, + &cb_intersection_request_alice, + s); if (NULL == s->intersection_listen) { s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE; @@ -1125,7 +1118,7 @@ handle_alice_client_message_multipart ( struct AliceServiceSession *s = cls; uint32_t contained_count; const struct GNUNET_SCALARPRODUCT_Element *elements; - struct GNUNET_SET_Element set_elem; + struct GNUNET_SETI_Element set_elem; struct GNUNET_SCALARPRODUCT_Element *elem; contained_count = ntohl (msg->element_count_contained); @@ -1150,7 +1143,7 @@ handle_alice_client_message_multipart ( set_elem.data = &elem->key; set_elem.size = sizeof(elem->key); set_elem.element_type = 0; - GNUNET_SET_add_element (s->intersection_set, &set_elem, NULL, NULL); + GNUNET_SETI_add_element (s->intersection_set, &set_elem, NULL, NULL); s->used_element_count++; } GNUNET_SERVICE_client_continue (s->client); @@ -1217,7 +1210,7 @@ handle_alice_client_message (void *cls, uint32_t contained_count; uint32_t total_count; const struct GNUNET_SCALARPRODUCT_Element *elements; - struct GNUNET_SET_Element set_elem; + struct GNUNET_SETI_Element set_elem; struct GNUNET_SCALARPRODUCT_Element *elem; total_count = ntohl (msg->element_count_total); @@ -1230,8 +1223,7 @@ handle_alice_client_message (void *cls, elements = (const struct GNUNET_SCALARPRODUCT_Element *) &msg[1]; s->intersected_elements = GNUNET_CONTAINER_multihashmap_create (s->total, GNUNET_YES); - s->intersection_set = - GNUNET_SET_create (cfg, GNUNET_SET_OPERATION_INTERSECTION); + s->intersection_set = GNUNET_SETI_create (cfg); for (uint32_t i = 0; i < contained_count; i++) { @@ -1255,7 +1247,10 @@ handle_alice_client_message (void *cls, set_elem.data = &elem->key; set_elem.size = sizeof(elem->key); set_elem.element_type = 0; - GNUNET_SET_add_element (s->intersection_set, &set_elem, NULL, NULL); + GNUNET_SETI_add_element (s->intersection_set, + &set_elem, + NULL, + NULL); s->used_element_count++; } GNUNET_SERVICE_client_continue (s->client); diff --git a/src/scalarproduct/gnunet-service-scalarproduct_bob.c b/src/scalarproduct/gnunet-service-scalarproduct_bob.c index c000749af..b0299779d 100644 --- a/src/scalarproduct/gnunet-service-scalarproduct_bob.c +++ b/src/scalarproduct/gnunet-service-scalarproduct_bob.c @@ -32,7 +32,7 @@ #include "gnunet_applications.h" #include "gnunet_protocols.h" #include "gnunet_scalarproduct_service.h" -#include "gnunet_set_service.h" +#include "gnunet_seti_service.h" #include "scalarproduct.h" #include "gnunet-service-scalarproduct.h" @@ -88,13 +88,13 @@ struct BobServiceSession * Set of elements for which we will be conducting an intersection. * The resulting elements are then used for computing the scalar product. */ - struct GNUNET_SET_Handle *intersection_set; + struct GNUNET_SETI_Handle *intersection_set; /** * Set of elements for which will conduction an intersection. * the resulting elements are then used for computing the scalar product. */ - struct GNUNET_SET_OperationHandle *intersection_op; + struct GNUNET_SETI_OperationHandle *intersection_op; /** * CADET port we are listening on. @@ -277,12 +277,12 @@ destroy_service_session (struct BobServiceSession *s) } if (NULL != s->intersection_op) { - GNUNET_SET_operation_cancel (s->intersection_op); + GNUNET_SETI_operation_cancel (s->intersection_op); s->intersection_op = NULL; } if (NULL != s->intersection_set) { - GNUNET_SET_destroy (s->intersection_set); + GNUNET_SETI_destroy (s->intersection_set); s->intersection_set = NULL; } if (NULL != s->e_a) @@ -888,22 +888,22 @@ handle_alices_cryptodata_message (void *cls, * that needs to be removed from the result set. * * @param cls closure with the `struct BobServiceSession` - * @param element a result element, only valid if status is #GNUNET_SET_STATUS_OK + * @param element a result element, only valid if status is #GNUNET_SETI_STATUS_OK * @param current_size current set size * @param status what has happened with the set intersection? */ static void cb_intersection_element_removed (void *cls, - const struct GNUNET_SET_Element *element, + const struct GNUNET_SETI_Element *element, uint64_t current_size, - enum GNUNET_SET_Status status) + enum GNUNET_SETI_Status status) { struct BobServiceSession *s = cls; struct GNUNET_SCALARPRODUCT_Element *se; switch (status) { - case GNUNET_SET_STATUS_OK: + case GNUNET_SETI_STATUS_DEL_LOCAL: /* this element has been removed from the set */ se = GNUNET_CONTAINER_multihashmap_get (s->intersected_elements, element->data); @@ -919,8 +919,7 @@ cb_intersection_element_removed (void *cls, se)); GNUNET_free (se); return; - - case GNUNET_SET_STATUS_DONE: + case GNUNET_SETI_STATUS_DONE: s->intersection_op = NULL; GNUNET_break (NULL == s->intersection_set); GNUNET_CADET_receive_done (s->channel); @@ -935,26 +934,19 @@ cb_intersection_element_removed (void *cls, transmit_cryptographic_reply (s); } return; - - case GNUNET_SET_STATUS_HALF_DONE: - /* unexpected for intersection */ - GNUNET_break (0); - return; - - case GNUNET_SET_STATUS_FAILURE: + case GNUNET_SETI_STATUS_FAILURE: /* unhandled status code */ LOG (GNUNET_ERROR_TYPE_DEBUG, "Set intersection failed!\n"); s->intersection_op = NULL; if (NULL != s->intersection_set) { - GNUNET_SET_destroy (s->intersection_set); + GNUNET_SETI_destroy (s->intersection_set); s->intersection_set = NULL; } s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE; prepare_client_end_notification (s); return; - default: GNUNET_break (0); return; @@ -977,23 +969,22 @@ start_intersection (struct BobServiceSession *s) (unsigned int) s->total); s->intersection_op - = GNUNET_SET_prepare (&s->peer, - &s->session_id, - NULL, - GNUNET_SET_RESULT_REMOVED, - (struct GNUNET_SET_Option[]) { { 0 } }, - &cb_intersection_element_removed, - s); + = GNUNET_SETI_prepare (&s->peer, + &s->session_id, + NULL, + (struct GNUNET_SETI_Option[]) { { 0 } }, + &cb_intersection_element_removed, + s); if (GNUNET_OK != - GNUNET_SET_commit (s->intersection_op, - s->intersection_set)) + GNUNET_SETI_commit (s->intersection_op, + s->intersection_set)) { GNUNET_break (0); s->status = GNUNET_SCALARPRODUCT_STATUS_FAILURE; prepare_client_end_notification (s); return; } - GNUNET_SET_destroy (s->intersection_set); + GNUNET_SETI_destroy (s->intersection_set); s->intersection_set = NULL; } @@ -1096,7 +1087,7 @@ handle_bob_client_message_multipart (void *cls, struct BobServiceSession *s = cls; uint32_t contained_count; const struct GNUNET_SCALARPRODUCT_Element *elements; - struct GNUNET_SET_Element set_elem; + struct GNUNET_SETI_Element set_elem; struct GNUNET_SCALARPRODUCT_Element *elem; contained_count = ntohl (msg->element_count_contained); @@ -1120,9 +1111,9 @@ handle_bob_client_message_multipart (void *cls, set_elem.data = &elem->key; set_elem.size = sizeof(elem->key); set_elem.element_type = 0; - GNUNET_SET_add_element (s->intersection_set, - &set_elem, - NULL, NULL); + GNUNET_SETI_add_element (s->intersection_set, + &set_elem, + NULL, NULL); } s->client_received_element_count += contained_count; GNUNET_SERVICE_client_continue (s->client); @@ -1206,7 +1197,7 @@ handle_bob_client_message (void *cls, uint32_t contained_count; uint32_t total_count; const struct GNUNET_SCALARPRODUCT_Element *elements; - struct GNUNET_SET_Element set_elem; + struct GNUNET_SETI_Element set_elem; struct GNUNET_SCALARPRODUCT_Element *elem; total_count = ntohl (msg->element_count_total); @@ -1220,9 +1211,7 @@ handle_bob_client_message (void *cls, s->intersected_elements = GNUNET_CONTAINER_multihashmap_create (s->total, GNUNET_YES); - s->intersection_set - = GNUNET_SET_create (cfg, - GNUNET_SET_OPERATION_INTERSECTION); + s->intersection_set = GNUNET_SETI_create (cfg); for (uint32_t i = 0; i < contained_count; i++) { if (0 == GNUNET_ntohll (elements[i].value)) @@ -1244,9 +1233,9 @@ handle_bob_client_message (void *cls, set_elem.data = &elem->key; set_elem.size = sizeof(elem->key); set_elem.element_type = 0; - GNUNET_SET_add_element (s->intersection_set, - &set_elem, - NULL, NULL); + GNUNET_SETI_add_element (s->intersection_set, + &set_elem, + NULL, NULL); s->used_element_count++; } GNUNET_SERVICE_client_continue (s->client); -- cgit v1.2.3 From 598375363d2b491d97972cb4ec10ebe72343716c Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 19 Aug 2020 11:05:43 +0200 Subject: -fix ftbfs --- src/Makefile.am | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Makefile.am b/src/Makefile.am index 446b1aa2a..234a63389 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -89,6 +89,8 @@ SUBDIRS = \ namestore \ cadet \ set \ + seti \ + setu \ consensus \ scalarproduct \ revocation \ -- cgit v1.2.3 From 6c1e93877194aa2c0cbd50a3636a1532dd521e3b Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 19 Aug 2020 11:15:13 +0200 Subject: -add API todo --- src/include/gnunet_setu_service.h | 13 +++++++++++++ src/setu/.gitignore | 6 ++++++ 2 files changed, 19 insertions(+) create mode 100644 src/setu/.gitignore diff --git a/src/include/gnunet_setu_service.h b/src/include/gnunet_setu_service.h index d737b97c1..459a6156b 100644 --- a/src/include/gnunet_setu_service.h +++ b/src/include/gnunet_setu_service.h @@ -84,6 +84,13 @@ enum GNUNET_SETU_Status */ GNUNET_SETU_STATUS_ADD_LOCAL, + /** + * Element should be added to the result set of the remote peer, i.e. the + * remote peer is missing an element. Only used if + * #GNUNET_SETU_OPTION_SYMMETRIC is set. + */ + GNUNET_SETU_STATUS_ADD_REMOTE, + /** * The other peer refused to do the operation with us, or something went * wrong. @@ -152,6 +159,12 @@ enum GNUNET_SETU_OptionType * detection. */ GNUNET_SETU_OPTION_FORCE_DELTA=4, + + /** + * Notify client also if we are sending a value to the other peer. + * FIXME: not implemented! + */ + GNUNET_SETU_OPTION_SYMMETRIC = 8 }; diff --git a/src/setu/.gitignore b/src/setu/.gitignore new file mode 100644 index 000000000..35295449b --- /dev/null +++ b/src/setu/.gitignore @@ -0,0 +1,6 @@ +gnunet-setu-profiler +gnunet-service-setu +gnunet-setu-ibf-profiler +test_setu_api +test_setu_copy +test_setu_result_symmetric -- cgit v1.2.3 From 6531e03875f8b3dba0eface2ac8ea195e9a80d86 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 19 Aug 2020 13:17:01 +0200 Subject: break out chapters on SETI and SETUI from SET chapter --- doc/handbook/chapters/developer.texi | 522 ++++++++++++++++++++++++++++++++++- 1 file changed, 520 insertions(+), 2 deletions(-) diff --git a/doc/handbook/chapters/developer.texi b/doc/handbook/chapters/developer.texi index 6d8ddd3c2..0ff222f25 100644 --- a/doc/handbook/chapters/developer.texi +++ b/doc/handbook/chapters/developer.texi @@ -71,6 +71,8 @@ new chapters, sections or insightful comments. * PEERINFO Subsystem:: * PEERSTORE Subsystem:: * SET Subsystem:: +* SETI Subsystem:: +* SETU Subsystem:: * STATISTICS Subsystem:: * Distributed Hash Table (DHT):: * GNU Name System (GNS):: @@ -6535,10 +6537,13 @@ destroyed as well. @node SET Subsystem @section SET Subsystem - +The SET subsystem is in process of being replaced by the SETU and +SETI subsystems, which provide basically the same functionality, +just using two different subsystems. SETI and SETU should be used +for new code. The SET service implements efficient set operations between two peers -over a mesh tunnel. +over a CADET tunnel. Currently, set union and set intersection are the only supported operations. Elements of a set consist of an @emph{element type} and arbitrary binary @emph{data}. @@ -6907,6 +6912,519 @@ All Bloom filter operations use a salt to mingle keys before hashing them into buckets, such that future iterations have a fresh chance of succeeding if they failed due to collisions before. + + + + + + + +@cindex SETI Subsystem +@node SETI Subsystem +@section SETI Subsystem + +The SET service implements efficient set intersection between two peers +over a CADET tunnel. +Elements of a set consist of an @emph{element type} and +arbitrary binary @emph{data}. +The size of an element's data is limited to around 62 KB. + +@menu +* Local Sets:: +* Set Modifications:: +* Set Operations:: +* Result Elements:: +* libgnunetseti:: +* The SET Client-Service Protocol:: +* The SET Intersection Peer-to-Peer Protocol:: +@end menu + +@node Local Sets +@subsection Local Sets + +Sets created by a local client can be modified (by adding additional elements) +and reused for multiple operations. If elements are to be removed, a fresh +set must be created by the client. + +@node Set Modifications +@subsection Set Modifications + +Even when set operations are active, one can add elements +to a set. +However, these changes will only be visible to operations that have been +created after the changes have taken place. That is, every set operation +only sees a snapshot of the set from the time the operation was started. +This mechanism is @emph{not} implemented by copying the whole set, but by +attaching @emph{generation information} to each element and operation. + +@node Set Operations +@subsection Set Operations + +Set operations can be started in two ways: Either by accepting an +operation request from a remote peer, or by requesting a set operation +from a remote peer. +Set operations are uniquely identified by the involved @emph{peers}, an +@emph{application id} and the @emph{operation type}. + +The client is notified of incoming set operations by @emph{set listeners}. +A set listener listens for incoming operations of a specific operation +type and application id. +Once notified of an incoming set request, the client can accept the set +request (providing a local set for the operation) or reject it. + +@node Result Elements +@subsection Result Elements + +The SET service has two @emph{result modes} that determine how an +operation's result set is delivered to the client: + +@itemize @bullet +@item @strong{Return intersection.} All elements of set resulting from the set +intersection are returned to the client. +@item @strong{Removed Elements.} Only elements that are in the local +peer's initial set but not in the intersection are returned. +@end itemize + +@cindex libgnunetseti +@node libgnunetseti +@subsection libgnunetseti + +@menu +* Sets:: +* Listeners:: +* Operations:: +* Supplying a Set:: +* The Result Callback:: +@end menu + +@node Sets +@subsubsection Sets + +New sets are created with @code{GNUNET_SETI_create}. Only the local peer's +configuration (as each set has its own client connection) must be provided. +The set exists until either the client calls @code{GNUNET_SET_destroy} or +the client's connection to the service is disrupted. +In the latter case, the client is notified by the return value of +functions dealing with sets. This return value must always be checked. + +Elements are added with @code{GNUNET_SET_add_element}. + +@node Listeners +@subsubsection Listeners + +Listeners are created with @code{GNUNET_SET_listen}. Each time time a +remote peer suggests a set operation with an application id and operation +type matching a listener, the listener's callback is invoked. +The client then must synchronously call either @code{GNUNET_SET_accept} +or @code{GNUNET_SET_reject}. Note that the operation will not be started +until the client calls @code{GNUNET_SET_commit} +(see Section "Supplying a Set"). + +@node Operations +@subsubsection Operations + +Operations to be initiated by the local peer are created with +@code{GNUNET_SET_prepare}. Note that the operation will not be started +until the client calls @code{GNUNET_SET_commit} +(see Section "Supplying a Set"). + +@node Supplying a Set +@subsubsection Supplying a Set + +To create symmetry between the two ways of starting a set operation +(accepting and initiating it), the operation handles returned by +@code{GNUNET_SET_accept} and @code{GNUNET_SET_prepare} do not yet have a +set to operate on, thus they can not do any work yet. + +The client must call @code{GNUNET_SET_commit} to specify a set to use for +an operation. @code{GNUNET_SET_commit} may only be called once per set +operation. + +@node The Result Callback +@subsubsection The Result Callback + +Clients must specify both a result mode and a result callback with +@code{GNUNET_SET_accept} and @code{GNUNET_SET_prepare}. The result +callback with a status indicating either that an element was received, or +the operation failed or succeeded. +The interpretation of the received element depends on the result mode. +The callback needs to know which result mode it is used in, as the +arguments do not indicate if an element is part of the full result set, +or if it is in the difference between the original set and the final set. + +@node The SETI Client-Service Protocol +@subsection The SETI Client-Service Protocol + +@menu +* Creating Sets:: +* Listeners2:: +* Initiating Operations:: +* Modifying Sets:: +* Results and Operation Status:: +@end menu + +@node Creating Sets +@subsubsection Creating Sets + +For each set of a client, there exists a client connection to the service. +Sets are created by sending the @code{GNUNET_SERVICE_SETI_CREATE} message +over a new client connection. Multiple operations for one set are +multiplexed over one client connection, using a request id supplied by +the client. + +@node Listeners2 +@subsubsection Listeners2 + + + +Each listener also requires a seperate client connection. By sending the +@code{GNUNET_SERVICE_SETI_LISTEN} message, the client notifies the service +of the application id and operation type it is interested in. A client +rejects an incoming request by sending @code{GNUNET_SERVICE_SETI_REJECT} +on the listener's client connection. +In contrast, when accepting an incoming request, a +@code{GNUNET_SERVICE_SETI_ACCEPT} message must be sent over the@ set that +is supplied for the set operation. + +@node Initiating Operations +@subsubsection Initiating Operations + +Operations with remote peers are initiated by sending a +@code{GNUNET_SERVICE_SETI_EVALUATE} message to the service. The@ client +connection that this message is sent by determines the set to use. + +@node Modifying Sets +@subsubsection Modifying Sets + +Sets are modified with the @code{GNUNET_SERVICE_SETI_ADD} message. + + +@c %@menu +@c %* Results and Operation Status:: +@c %* Iterating Sets:: +@c %@end menu + +@node Results and Operation Status +@subsubsection Results and Operation Status + +The service notifies the client of result elements and success/failure of +a set operation with the @code{GNUNET_SERVICE_SETI_RESULT} message. + +@node The SET Intersection Peer-to-Peer Protocol +@subsection The SET Intersection Peer-to-Peer Protocol + +The intersection protocol operates over CADET and starts with a +GNUNET_MESSAGE_TYPE_SETI_P2P_OPERATION_REQUEST being sent by the peer +initiating the operation to the peer listening for inbound requests. +It includes the number of elements of the initiating peer, which is used +to decide which side will send a Bloom filter first. + +The listening peer checks if the operation type and application +identifier are acceptable for its current state. +If not, it responds with a GNUNET_MESSAGE_TYPE_SETI_RESULT and a status of +GNUNET_SETI_STATUS_FAILURE (and terminates the CADET channel). + +If the application accepts the request, the listener sends back a +@code{GNUNET_MESSAGE_TYPE_SETI_P2P_ELEMENT_INFO} if it has +more elements in the set than the client. +Otherwise, it immediately starts with the Bloom filter exchange. +If the initiator receives a +@code{GNUNET_MESSAGE_TYPE_SETI_P2P_ELEMENT_INFO} response, +it beings the Bloom filter exchange, unless the set size is indicated to +be zero, in which case the intersection is considered finished after +just the initial handshake. + + +@menu +* The Bloom filter exchange:: +* Salt:: +@end menu + +@node The Bloom filter exchange +@subsubsection The Bloom filter exchange + +In this phase, each peer transmits a Bloom filter over the remaining +keys of the local set to the other peer using a +@code{GNUNET_MESSAGE_TYPE_SETI_P2P_BF} message. This +message additionally includes the number of elements left in the sender's +set, as well as the XOR over all of the keys in that set. + +The number of bits 'k' set per element in the Bloom filter is calculated +based on the relative size of the two sets. +Furthermore, the size of the Bloom filter is calculated based on 'k' and +the number of elements in the set to maximize the amount of data filtered +per byte transmitted on the wire (while avoiding an excessively high +number of iterations). + +The receiver of the message removes all elements from its local set that +do not pass the Bloom filter test. +It then checks if the set size of the sender and the XOR over the keys +match what is left of its own set. If they do, it sends a +@code{GNUNET_MESSAGE_TYPE_SETI_P2P_DONE} back to indicate +that the latest set is the final result. +Otherwise, the receiver starts another Bloom filter exchange, except +this time as the sender. + +@node Salt +@subsubsection Salt + +Bloomfilter operations are probabilistic: With some non-zero probability +the test may incorrectly say an element is in the set, even though it is +not. + +To mitigate this problem, the intersection protocol iterates exchanging +Bloom filters using a different random 32-bit salt in each iteration (the +salt is also included in the message). +With different salts, set operations may fail for different elements. +Merging the results from the executions, the probability of failure drops +to zero. + +The iterations terminate once both peers have established that they have +sets of the same size, and where the XOR over all keys computes the same +512-bit value (leaving a failure probability of 2-511). + + +@cindex SETU Subsystem +@node SETU Subsystem +@section SETU Subsystem + +The SETU service implements efficient set union operations between two peers +over a CADET tunnel. Elements of a set consist of an @emph{element type} and +arbitrary binary @emph{data}. The size of an element's data is limited to +around 62 KB. + +@menu +* Local Sets:: +* Set Modifications:: +* Set Operations:: +* Result Elements:: +* libgnunetset:: +* The SET Client-Service Protocol:: +* The SET Intersection Peer-to-Peer Protocol:: +* The SET Union Peer-to-Peer Protocol:: +@end menu + +@node Local Sets +@subsection Local Sets + +Sets created by a local client can be modified (by adding additional elements) +and reused for multiple operations. If elements are to be removed, a fresh +set must be created by the client. + +@node Set Modifications +@subsection Set Modifications + +Even when set operations are active, one can add elements +to a set. +However, these changes will only be visible to operations that have been +created after the changes have taken place. That is, every set operation +only sees a snapshot of the set from the time the operation was started. +This mechanism is @emph{not} implemented by copying the whole set, but by +attaching @emph{generation information} to each element and operation. + +@node Set Operations +@subsection Set Operations + +Set operations can be started in two ways: Either by accepting an +operation request from a remote peer, or by requesting a set operation +from a remote peer. +Set operations are uniquely identified by the involved @emph{peers}, an +@emph{application id} and the @emph{operation type}. + +The client is notified of incoming set operations by @emph{set listeners}. +A set listener listens for incoming operations of a specific operation +type and application id. +Once notified of an incoming set request, the client can accept the set +request (providing a local set for the operation) or reject it. + +@node Result Elements +@subsection Result Elements + +The SET service has three @emph{result modes} that determine how an +operation's result set is delivered to the client: + +@itemize @bullet +@item @strong{Locally added Elements.} Elements that are in the union +but not already in the local peer's set are returned. +@item @strong{Remote added Elements.} Additionally, notify the client +if the remote peer lacked some elements and thus also return to the +local client those elements that we are sending to the remote peer to +be added to its union. Obtaining these elements requires setting +the @code{GNUNET_SETU_OPTION_SYMMETRIC} option. +@end itemize + +@cindex libgnunetsetu +@node libgnunetsetu +@subsection libgnunetsetu + +@menu +* Sets:: +* Listeners:: +* Operations:: +* Supplying a Set:: +* The Result Callback:: +@end menu + +@node Sets +@subsubsection Sets + +New sets are created with @code{GNUNET_SETU_create}. Only the local peer's +configuration (as each set has its own client connection) must be provided. +The set exists until either the client calls @code{GNUNET_SETU_destroy} or +the client's connection to the service is disrupted. +In the latter case, the client is notified by the return value of +functions dealing with sets. This return value must always be checked. + +Elements are added with @code{GNUNET_SETU_add_element}. + +@node Listeners +@subsubsection Listeners + +Listeners are created with @code{GNUNET_SETU_listen}. Each time time a +remote peer suggests a set operation with an application id and operation +type matching a listener, the listener's callback is invoked. +The client then must synchronously call either @code{GNUNET_SETU_accept} +or @code{GNUNET_SETU_reject}. Note that the operation will not be started +until the client calls @code{GNUNET_SETU_commit} +(see Section "Supplying a Set"). + +@node Operations +@subsubsection Operations + +Operations to be initiated by the local peer are created with +@code{GNUNET_SETU_prepare}. Note that the operation will not be started +until the client calls @code{GNUNET_SETU_commit} +(see Section "Supplying a Set"). + +@node Supplying a Set +@subsubsection Supplying a Set + +To create symmetry between the two ways of starting a set operation +(accepting and initiating it), the operation handles returned by +@code{GNUNET_SETU_accept} and @code{GNUNET_SETU_prepare} do not yet have a +set to operate on, thus they can not do any work yet. + +The client must call @code{GNUNET_SETU_commit} to specify a set to use for +an operation. @code{GNUNET_SETU_commit} may only be called once per set +operation. + +@node The Result Callback +@subsubsection The Result Callback + +Clients must specify both a result mode and a result callback with +@code{GNUNET_SETU_accept} and @code{GNUNET_SETU_prepare}. The result +callback with a status indicating either that an element was received, +transmitted to the other peer (if this information was requested), or +if the operation failed or ultimately succeeded. + +@node The SETU Client-Service Protocol +@subsection The SETU Client-Service Protocol + +@menu +* Creating Sets:: +* Listeners2:: +* Initiating Operations:: +* Modifying Sets:: +* Results and Operation Status:: +* Iterating Sets:: +@end menu + +@node Creating Sets +@subsubsection Creating Sets + +For each set of a client, there exists a client connection to the service. +Sets are created by sending the @code{GNUNET_SERVICE_SETU_CREATE} message +over a new client connection. Multiple operations for one set are +multiplexed over one client connection, using a request id supplied by +the client. + +@node Listeners2 +@subsubsection Listeners2 + +Each listener also requires a seperate client connection. By sending the +@code{GNUNET_SERVICE_SETU_LISTEN} message, the client notifies the service +of the application id and operation type it is interested in. A client +rejects an incoming request by sending @code{GNUNET_SERVICE_SETU_REJECT} +on the listener's client connection. +In contrast, when accepting an incoming request, a +@code{GNUNET_SERVICE_SETU_ACCEPT} message must be sent over the@ set that +is supplied for the set operation. + +@node Initiating Operations +@subsubsection Initiating Operations + + + +Operations with remote peers are initiated by sending a +@code{GNUNET_SERVICE_SETU_EVALUATE} message to the service. The@ client +connection that this message is sent by determines the set to use. + +@node Modifying Sets +@subsubsection Modifying Sets + + + +Sets are modified with the @code{GNUNET_SERVICE_SETU_ADD} message. + + +@c %@menu +@c %* Results and Operation Status:: +@c %* Iterating Sets:: +@c %@end menu + +@node Results and Operation Status +@subsubsection Results and Operation Status + +The service notifies the client of result elements and success/failure of +a set operation with the @code{GNUNET_SERVICE_SETU_RESULT} message. + + +@node The SET Union Peer-to-Peer Protocol +@subsection The SET Union Peer-to-Peer Protocol + + +The SET union protocol is based on Eppstein's efficient set reconciliation +without prior context. You should read this paper first if you want to +understand the protocol. + +The union protocol operates over CADET and starts with a +GNUNET_MESSAGE_TYPE_SETU_P2P_OPERATION_REQUEST being sent by the peer +initiating the operation to the peer listening for inbound requests. +It includes the number of elements of the initiating peer, which is +currently not used. + +The listening peer checks if the operation type and application +identifier are acceptable for its current state. If not, it responds with +a @code{GNUNET_MESSAGE_TYPE_SETU_RESULT} and a status of +@code{GNUNET_SETU_STATUS_FAILURE} (and terminates the CADET channel). + +If the application accepts the request, it sends back a strata estimator +using a message of type GNUNET_MESSAGE_TYPE_SETU_P2P_SE. The +initiator evaluates the strata estimator and initiates the exchange of +invertible Bloom filters, sending a GNUNET_MESSAGE_TYPE_SETU_P2P_IBF. + +During the IBF exchange, if the receiver cannot invert the Bloom filter or +detects a cycle, it sends a larger IBF in response (up to a defined +maximum limit; if that limit is reached, the operation fails). +Elements decoded while processing the IBF are transmitted to the other +peer using GNUNET_MESSAGE_TYPE_SETU_P2P_ELEMENTS, or requested from the +other peer using GNUNET_MESSAGE_TYPE_SETU_P2P_ELEMENT_REQUESTS messages, +depending on the sign observed during decoding of the IBF. +Peers respond to a GNUNET_MESSAGE_TYPE_SETU_P2P_ELEMENT_REQUESTS message +with the respective element in a GNUNET_MESSAGE_TYPE_SETU_P2P_ELEMENTS +message. If the IBF fully decodes, the peer responds with a +GNUNET_MESSAGE_TYPE_SETU_P2P_DONE message instead of another +GNUNET_MESSAGE_TYPE_SETU_P2P_IBF. + +All Bloom filter operations use a salt to mingle keys before hashing them +into buckets, such that future iterations have a fresh chance of +succeeding if they failed due to collisions before. + + + + + + @cindex STATISTICS Subsystem @node STATISTICS Subsystem @section STATISTICS Subsystem -- cgit v1.2.3 From 28bd10098bf571c0fa2cc988d7422528c0d68877 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 19 Aug 2020 13:17:57 +0200 Subject: -document seti/setu in structure --- doc/handbook/images/structure.dot | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/handbook/images/structure.dot b/doc/handbook/images/structure.dot index a53db90b8..f3cf193d8 100644 --- a/doc/handbook/images/structure.dot +++ b/doc/handbook/images/structure.dot @@ -52,7 +52,7 @@ splines = true; gns -> dnsstub; gns -> identity; revocation -> core; - revocation -> set; + revocation -> setu; namestore -> identity; namestore -> gnsrecord; dnsparser -> gnsrecord [style=dotted,color=blue]; @@ -96,9 +96,11 @@ splines = true; transport -> fragmentation; consensus -> set; consensus -> cadet; - scalarproduct -> set; + scalarproduct -> seti; scalarproduct -> cadet; set -> cadet; + seti -> cadet; + setu -> cadet; peerinfo -> hello; fragmentation [shape=diamond]; hello [shape=diamond]; -- cgit v1.2.3 From 608f3502de2bc0745e9e8078e048cf9ac86928c8 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 19 Aug 2020 13:30:25 +0200 Subject: -fix links --- doc/handbook/chapters/developer.texi | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/doc/handbook/chapters/developer.texi b/doc/handbook/chapters/developer.texi index 0ff222f25..45f3f1afa 100644 --- a/doc/handbook/chapters/developer.texi +++ b/doc/handbook/chapters/developer.texi @@ -6935,8 +6935,8 @@ The size of an element's data is limited to around 62 KB. * Set Operations:: * Result Elements:: * libgnunetseti:: -* The SET Client-Service Protocol:: -* The SET Intersection Peer-to-Peer Protocol:: +* The SETI Client-Service Protocol:: +* The SETI Intersection Peer-to-Peer Protocol:: @end menu @node Local Sets @@ -7110,8 +7110,8 @@ Sets are modified with the @code{GNUNET_SERVICE_SETI_ADD} message. The service notifies the client of result elements and success/failure of a set operation with the @code{GNUNET_SERVICE_SETI_RESULT} message. -@node The SET Intersection Peer-to-Peer Protocol -@subsection The SET Intersection Peer-to-Peer Protocol +@node The SETI Intersection Peer-to-Peer Protocol +@subsection The SETI Intersection Peer-to-Peer Protocol The intersection protocol operates over CADET and starts with a GNUNET_MESSAGE_TYPE_SETI_P2P_OPERATION_REQUEST being sent by the peer @@ -7198,10 +7198,9 @@ around 62 KB. * Set Modifications:: * Set Operations:: * Result Elements:: -* libgnunetset:: -* The SET Client-Service Protocol:: -* The SET Intersection Peer-to-Peer Protocol:: -* The SET Union Peer-to-Peer Protocol:: +* libgnunetsetu:: +* The SETU Client-Service Protocol:: +* The SETU Union Peer-to-Peer Protocol:: @end menu @node Local Sets @@ -7379,8 +7378,8 @@ The service notifies the client of result elements and success/failure of a set operation with the @code{GNUNET_SERVICE_SETU_RESULT} message. -@node The SET Union Peer-to-Peer Protocol -@subsection The SET Union Peer-to-Peer Protocol +@node The SETU Union Peer-to-Peer Protocol +@subsection The SETU Union Peer-to-Peer Protocol The SET union protocol is based on Eppstein's efficient set reconciliation -- cgit v1.2.3 From aa2b84ff8caed28aec31e9a39375a02fceeb4e8c Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 19 Aug 2020 13:40:28 +0200 Subject: -fix more links --- doc/handbook/chapters/developer.texi | 187 +++++++++++++++++------------------ 1 file changed, 91 insertions(+), 96 deletions(-) diff --git a/doc/handbook/chapters/developer.texi b/doc/handbook/chapters/developer.texi index 45f3f1afa..369e5327c 100644 --- a/doc/handbook/chapters/developer.texi +++ b/doc/handbook/chapters/developer.texi @@ -6930,24 +6930,24 @@ arbitrary binary @emph{data}. The size of an element's data is limited to around 62 KB. @menu -* Local Sets:: -* Set Modifications:: -* Set Operations:: -* Result Elements:: +* Intersection Sets:: +* Set Intersection Modifications:: +* Set Intersection Operations:: +* Intersection Result Elements:: * libgnunetseti:: * The SETI Client-Service Protocol:: * The SETI Intersection Peer-to-Peer Protocol:: @end menu -@node Local Sets -@subsection Local Sets +@node Intersection Sets +@subsection Intersection Sets Sets created by a local client can be modified (by adding additional elements) and reused for multiple operations. If elements are to be removed, a fresh set must be created by the client. -@node Set Modifications -@subsection Set Modifications +@node Set Intersection Modifications +@subsection Set Intersection Modifications Even when set operations are active, one can add elements to a set. @@ -6957,8 +6957,8 @@ only sees a snapshot of the set from the time the operation was started. This mechanism is @emph{not} implemented by copying the whole set, but by attaching @emph{generation information} to each element and operation. -@node Set Operations -@subsection Set Operations +@node Set Intersection Operations +@subsection Set Intersection Operations Set operations can be started in two ways: Either by accepting an operation request from a remote peer, or by requesting a set operation @@ -6972,8 +6972,8 @@ type and application id. Once notified of an incoming set request, the client can accept the set request (providing a local set for the operation) or reject it. -@node Result Elements -@subsection Result Elements +@node Intersection Result Elements +@subsection Intersection Result Elements The SET service has two @emph{result modes} that determine how an operation's result set is delivered to the client: @@ -6990,15 +6990,15 @@ peer's initial set but not in the intersection are returned. @subsection libgnunetseti @menu -* Sets:: -* Listeners:: -* Operations:: -* Supplying a Set:: -* The Result Callback:: +* Intersection Set API:: +* Intersection Listeners:: +* Intersection Operations:: +* Supplying a Set for Intersection:: +* The Intersection Result Callback:: @end menu -@node Sets -@subsubsection Sets +@node Intersection Set API +@subsubsection Intersection Set API New sets are created with @code{GNUNET_SETI_create}. Only the local peer's configuration (as each set has its own client connection) must be provided. @@ -7009,8 +7009,8 @@ functions dealing with sets. This return value must always be checked. Elements are added with @code{GNUNET_SET_add_element}. -@node Listeners -@subsubsection Listeners +@node Intersection Listeners +@subsubsection Intersection Listeners Listeners are created with @code{GNUNET_SET_listen}. Each time time a remote peer suggests a set operation with an application id and operation @@ -7020,16 +7020,16 @@ or @code{GNUNET_SET_reject}. Note that the operation will not be started until the client calls @code{GNUNET_SET_commit} (see Section "Supplying a Set"). -@node Operations -@subsubsection Operations +@node Intersection Operations +@subsubsection Intersection Operations Operations to be initiated by the local peer are created with @code{GNUNET_SET_prepare}. Note that the operation will not be started until the client calls @code{GNUNET_SET_commit} (see Section "Supplying a Set"). -@node Supplying a Set -@subsubsection Supplying a Set +@node Supplying a Set for Intersection +@subsubsection Supplying a Set for Intersection To create symmetry between the two ways of starting a set operation (accepting and initiating it), the operation handles returned by @@ -7040,8 +7040,8 @@ The client must call @code{GNUNET_SET_commit} to specify a set to use for an operation. @code{GNUNET_SET_commit} may only be called once per set operation. -@node The Result Callback -@subsubsection The Result Callback +@node The Intersection Result Callback +@subsubsection The Intersection Result Callback Clients must specify both a result mode and a result callback with @code{GNUNET_SET_accept} and @code{GNUNET_SET_prepare}. The result @@ -7056,15 +7056,15 @@ or if it is in the difference between the original set and the final set. @subsection The SETI Client-Service Protocol @menu -* Creating Sets:: -* Listeners2:: -* Initiating Operations:: -* Modifying Sets:: -* Results and Operation Status:: +* Creating Intersection Sets:: +* Listeners for Intersection:: +* Initiating Intersection Operations:: +* Modifying Intersection Sets:: +* Intersection Results and Operation Status:: @end menu -@node Creating Sets -@subsubsection Creating Sets +@node Creating Intersection Sets +@subsubsection Creating Intersection Sets For each set of a client, there exists a client connection to the service. Sets are created by sending the @code{GNUNET_SERVICE_SETI_CREATE} message @@ -7072,10 +7072,8 @@ over a new client connection. Multiple operations for one set are multiplexed over one client connection, using a request id supplied by the client. -@node Listeners2 -@subsubsection Listeners2 - - +@node Listeners for Intersection +@subsubsection Listeners for Intersection Each listener also requires a seperate client connection. By sending the @code{GNUNET_SERVICE_SETI_LISTEN} message, the client notifies the service @@ -7086,15 +7084,15 @@ In contrast, when accepting an incoming request, a @code{GNUNET_SERVICE_SETI_ACCEPT} message must be sent over the@ set that is supplied for the set operation. -@node Initiating Operations -@subsubsection Initiating Operations +@node Initiating Intersection Operations +@subsubsection Initiating Intersection Operations Operations with remote peers are initiated by sending a @code{GNUNET_SERVICE_SETI_EVALUATE} message to the service. The@ client connection that this message is sent by determines the set to use. -@node Modifying Sets -@subsubsection Modifying Sets +@node Modifying Intersection Sets +@subsubsection Modifying Intersection Sets Sets are modified with the @code{GNUNET_SERVICE_SETI_ADD} message. @@ -7104,8 +7102,8 @@ Sets are modified with the @code{GNUNET_SERVICE_SETI_ADD} message. @c %* Iterating Sets:: @c %@end menu -@node Results and Operation Status -@subsubsection Results and Operation Status +@node Intersection Results and Operation Status +@subsubsection Intersection Results and Operation Status The service notifies the client of result elements and success/failure of a set operation with the @code{GNUNET_SERVICE_SETI_RESULT} message. @@ -7136,12 +7134,12 @@ just the initial handshake. @menu -* The Bloom filter exchange:: -* Salt:: +* The Bloom filter exchange in SETI:: +* Intersection Salt:: @end menu -@node The Bloom filter exchange -@subsubsection The Bloom filter exchange +@node The Bloom filter exchange in SETI +@subsubsection The Bloom filter exchange in SETI In this phase, each peer transmits a Bloom filter over the remaining keys of the local set to the other peer using a @@ -7165,10 +7163,10 @@ that the latest set is the final result. Otherwise, the receiver starts another Bloom filter exchange, except this time as the sender. -@node Salt -@subsubsection Salt +@node Intersection Salt +@subsubsection Intersection Salt -Bloomfilter operations are probabilistic: With some non-zero probability +Bloom filter operations are probabilistic: With some non-zero probability the test may incorrectly say an element is in the set, even though it is not. @@ -7194,24 +7192,24 @@ arbitrary binary @emph{data}. The size of an element's data is limited to around 62 KB. @menu -* Local Sets:: -* Set Modifications:: -* Set Operations:: -* Result Elements:: +* Union Sets:: +* Set Union Modifications:: +* Set Union Operations:: +* Union Result Elements:: * libgnunetsetu:: * The SETU Client-Service Protocol:: * The SETU Union Peer-to-Peer Protocol:: @end menu -@node Local Sets -@subsection Local Sets +@node Union Sets +@subsection Union Sets Sets created by a local client can be modified (by adding additional elements) and reused for multiple operations. If elements are to be removed, a fresh set must be created by the client. -@node Set Modifications -@subsection Set Modifications +@node Set Union Modifications +@subsection Set Union Modifications Even when set operations are active, one can add elements to a set. @@ -7221,8 +7219,8 @@ only sees a snapshot of the set from the time the operation was started. This mechanism is @emph{not} implemented by copying the whole set, but by attaching @emph{generation information} to each element and operation. -@node Set Operations -@subsection Set Operations +@node Set Union Operations +@subsection Set Union Operations Set operations can be started in two ways: Either by accepting an operation request from a remote peer, or by requesting a set operation @@ -7236,8 +7234,8 @@ type and application id. Once notified of an incoming set request, the client can accept the set request (providing a local set for the operation) or reject it. -@node Result Elements -@subsection Result Elements +@node Union Result Elements +@subsection Union Result Elements The SET service has three @emph{result modes} that determine how an operation's result set is delivered to the client: @@ -7257,15 +7255,15 @@ the @code{GNUNET_SETU_OPTION_SYMMETRIC} option. @subsection libgnunetsetu @menu -* Sets:: -* Listeners:: -* Operations:: -* Supplying a Set:: -* The Result Callback:: +* Union Set API:: +* Union Listeners:: +* Union Operations:: +* Supplying a Set for Union:: +* The Union Result Callback:: @end menu -@node Sets -@subsubsection Sets +@node Union Set API +@subsubsection Union Set API New sets are created with @code{GNUNET_SETU_create}. Only the local peer's configuration (as each set has its own client connection) must be provided. @@ -7276,8 +7274,8 @@ functions dealing with sets. This return value must always be checked. Elements are added with @code{GNUNET_SETU_add_element}. -@node Listeners -@subsubsection Listeners +@node Union Listeners +@subsubsection Union Listeners Listeners are created with @code{GNUNET_SETU_listen}. Each time time a remote peer suggests a set operation with an application id and operation @@ -7287,16 +7285,16 @@ or @code{GNUNET_SETU_reject}. Note that the operation will not be started until the client calls @code{GNUNET_SETU_commit} (see Section "Supplying a Set"). -@node Operations -@subsubsection Operations +@node Union Operations +@subsubsection Union Operations Operations to be initiated by the local peer are created with @code{GNUNET_SETU_prepare}. Note that the operation will not be started until the client calls @code{GNUNET_SETU_commit} (see Section "Supplying a Set"). -@node Supplying a Set -@subsubsection Supplying a Set +@node Supplying a Set for Union +@subsubsection Supplying a Set for Union To create symmetry between the two ways of starting a set operation (accepting and initiating it), the operation handles returned by @@ -7307,8 +7305,8 @@ The client must call @code{GNUNET_SETU_commit} to specify a set to use for an operation. @code{GNUNET_SETU_commit} may only be called once per set operation. -@node The Result Callback -@subsubsection The Result Callback +@node The Union Result Callback +@subsubsection The Union Result Callback Clients must specify both a result mode and a result callback with @code{GNUNET_SETU_accept} and @code{GNUNET_SETU_prepare}. The result @@ -7320,16 +7318,15 @@ if the operation failed or ultimately succeeded. @subsection The SETU Client-Service Protocol @menu -* Creating Sets:: -* Listeners2:: -* Initiating Operations:: -* Modifying Sets:: -* Results and Operation Status:: -* Iterating Sets:: +* Creating Union Sets:: +* Listeners for Union:: +* Initiating Union Operations:: +* Modifying Union Sets:: +* Union Results and Operation Status:: @end menu -@node Creating Sets -@subsubsection Creating Sets +@node Creating Union Sets +@subsubsection Creating Union Sets For each set of a client, there exists a client connection to the service. Sets are created by sending the @code{GNUNET_SERVICE_SETU_CREATE} message @@ -7337,8 +7334,8 @@ over a new client connection. Multiple operations for one set are multiplexed over one client connection, using a request id supplied by the client. -@node Listeners2 -@subsubsection Listeners2 +@node Listeners for Union +@subsubsection Listeners for Union Each listener also requires a seperate client connection. By sending the @code{GNUNET_SERVICE_SETU_LISTEN} message, the client notifies the service @@ -7349,8 +7346,8 @@ In contrast, when accepting an incoming request, a @code{GNUNET_SERVICE_SETU_ACCEPT} message must be sent over the@ set that is supplied for the set operation. -@node Initiating Operations -@subsubsection Initiating Operations +@node Initiating Union Operations +@subsubsection Initiating Union Operations @@ -7358,10 +7355,8 @@ Operations with remote peers are initiated by sending a @code{GNUNET_SERVICE_SETU_EVALUATE} message to the service. The@ client connection that this message is sent by determines the set to use. -@node Modifying Sets -@subsubsection Modifying Sets - - +@node Modifying Union Sets +@subsubsection Modifying Union Sets Sets are modified with the @code{GNUNET_SERVICE_SETU_ADD} message. @@ -7371,8 +7366,8 @@ Sets are modified with the @code{GNUNET_SERVICE_SETU_ADD} message. @c %* Iterating Sets:: @c %@end menu -@node Results and Operation Status -@subsubsection Results and Operation Status +@node Union Results and Operation Status +@subsubsection Union Results and Operation Status The service notifies the client of result elements and success/failure of a set operation with the @code{GNUNET_SERVICE_SETU_RESULT} message. -- cgit v1.2.3 From 1ca1140d4602dcc5c66da0d1ab1b082db9258ead Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 19 Aug 2020 17:54:21 +0200 Subject: -implement the symmetric option in setu --- src/setu/gnunet-service-setu.c | 34 +++++++++++++++++++++++++--------- src/setu/setu.h | 15 +++++++++++++-- src/setu/setu_api.c | 31 ++++++++++++++++++++++++++++--- 3 files changed, 66 insertions(+), 14 deletions(-) diff --git a/src/setu/gnunet-service-setu.c b/src/setu/gnunet-service-setu.c index e61e1ab0c..326589186 100644 --- a/src/setu/gnunet-service-setu.c +++ b/src/setu/gnunet-service-setu.c @@ -370,6 +370,12 @@ struct Operation */ int byzantine; + /** + * #GNUNET_YES to also send back set elements we are sending to + * the remote peer. + */ + int symmetric; + /** * Lower bound for the set size, used only when * byzantine mode is enabled. @@ -1576,7 +1582,9 @@ decode_and_send (struct Operation *op) last_key = key; - res = ibf_decode (diff_ibf, &side, &key); + res = ibf_decode (diff_ibf, + &side, + &key); if (res == GNUNET_OK) { LOG (GNUNET_ERROR_TYPE_DEBUG, @@ -1839,7 +1847,7 @@ handle_union_p2p_ibf (void *cls, static void send_client_element (struct Operation *op, const struct GNUNET_SETU_Element *element, - int status) + enum GNUNET_SETU_Status status) { struct GNUNET_MQ_Envelope *ev; struct GNUNET_SETU_ResultMessage *rm; @@ -2071,8 +2079,8 @@ handle_union_p2p_full_element (void *cls, ee->element.data = &ee[1]; ee->element.element_type = ntohs (emsg->element_type); ee->remote = GNUNET_YES; - GNUNET_SETU_element_hash (&ee->element, &ee->element_hash); - + GNUNET_SETU_element_hash (&ee->element, + &ee->element_hash); LOG (GNUNET_ERROR_TYPE_DEBUG, "Got element (full diff, size %u, hash %s) from peer\n", (unsigned int) element_size, @@ -2285,7 +2293,6 @@ handle_union_p2p_full_done (void *cls, GNUNET_CONTAINER_multihashmap32_iterate (op->key_to_element, &send_missing_full_elements_iter, op); - ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SETU_P2P_FULL_DONE); GNUNET_MQ_send (op->mq, ev); @@ -2387,9 +2394,12 @@ handle_union_p2p_demand (void *cls, fail_union_operation (op); return; } - ev = GNUNET_MQ_msg_extra (emsg, ee->element.size, + ev = GNUNET_MQ_msg_extra (emsg, + ee->element.size, GNUNET_MESSAGE_TYPE_SETU_P2P_ELEMENTS); - GNUNET_memcpy (&emsg[1], ee->element.data, ee->element.size); + GNUNET_memcpy (&emsg[1], + ee->element.data, + ee->element.size); emsg->reserved = htons (0); emsg->element_type = htons (ee->element.element_type); LOG (GNUNET_ERROR_TYPE_DEBUG, @@ -2402,6 +2412,10 @@ handle_union_p2p_demand (void *cls, "# exchanged elements", 1, GNUNET_NO); + if (op->symmetric) + send_client_element (op, + &ee->element, + GNUNET_SET_STATUS_ADD_REMOTE); } GNUNET_CADET_receive_done (op->channel); } @@ -3316,9 +3330,10 @@ handle_client_evaluate (void *cls, op->peer = msg->target_peer; op->client_request_id = ntohl (msg->request_id); op->byzantine = msg->byzantine; - op->byzantine_lower_bound = msg->byzantine_lower_bound; + op->byzantine_lower_bound = ntohl (msg->byzantine_lower_bound); op->force_full = msg->force_full; op->force_delta = msg->force_delta; + op->symmetric = msg->symmetric; context = GNUNET_MQ_extract_nested_mh (msg); /* Advance generation values, so that @@ -3500,9 +3515,10 @@ handle_client_accept (void *cls, op); op->client_request_id = ntohl (msg->request_id); op->byzantine = msg->byzantine; - op->byzantine_lower_bound = msg->byzantine_lower_bound; + op->byzantine_lower_bound = ntohl (msg->byzantine_lower_bound); op->force_full = msg->force_full; op->force_delta = msg->force_delta; + op->symmetric = msg->symmetric; /* Advance generation values, so that future mutations do not interfer with the running operation. */ diff --git a/src/setu/setu.h b/src/setu/setu.h index f1c5df92b..e9a0def95 100644 --- a/src/setu/setu.h +++ b/src/setu/setu.h @@ -110,11 +110,17 @@ struct GNUNET_SETU_AcceptMessage */ uint8_t byzantine; + /** + * #GNUNET_YES to also send back set elements we are sending to + * the remote peer. + */ + uint8_t symmetric; + /** * Lower bound for the set size, used only when * byzantine mode is enabled. */ - uint8_t byzantine_lower_bound; + uint32_t byzantine_lower_bound; }; @@ -208,11 +214,16 @@ struct GNUNET_SETU_EvaluateMessage */ uint8_t byzantine; + /** + * Also return set elements we are sending to the remote peer. + */ + uint8_t symmetric; + /** * Lower bound for the set size, used only when * byzantine mode is enabled. */ - uint8_t byzantine_lower_bound; + uint32_t byzantine_lower_bound; /* rest: context message, that is, application-specific message to convince listener to pick up */ diff --git a/src/setu/setu_api.c b/src/setu/setu_api.c index 48260de55..dd3a4a769 100644 --- a/src/setu/setu_api.c +++ b/src/setu/setu_api.c @@ -235,8 +235,7 @@ handle_result (void *cls, switch (result_status) { case GNUNET_SETU_STATUS_ADD_LOCAL: - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Treating result as element\n"); + case GNUNET_SETU_STATUS_ADD_REMOTE: e.data = &msg[1]; e.size = ntohs (msg->header.size) - sizeof(struct GNUNET_SETU_ResultMessage); @@ -533,7 +532,7 @@ GNUNET_SETU_prepare (const struct GNUNET_PeerIdentity *other_peer, { case GNUNET_SETU_OPTION_BYZANTINE: msg->byzantine = GNUNET_YES; - msg->byzantine_lower_bound = opt->v.num; + msg->byzantine_lower_bound = htonl (opt->v.num); break; case GNUNET_SETU_OPTION_FORCE_FULL: msg->force_full = GNUNET_YES; @@ -541,6 +540,9 @@ GNUNET_SETU_prepare (const struct GNUNET_PeerIdentity *other_peer, case GNUNET_SETU_OPTION_FORCE_DELTA: msg->force_delta = GNUNET_YES; break; + case GNUNET_SETU_OPTION_SYMMETRIC: + msg->symmetric = GNUNET_YES; + break; default: LOG (GNUNET_ERROR_TYPE_ERROR, "Option with type %d not recognized\n", @@ -786,6 +788,29 @@ GNUNET_SETU_accept (struct GNUNET_SETU_Request *request, mqm = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SETU_ACCEPT); msg->accept_reject_id = htonl (request->accept_id); + for (const struct GNUNET_SETU_Option *opt = options; opt->type != 0; opt++) + { + switch (opt->type) + { + case GNUNET_SETU_OPTION_BYZANTINE: + msg->byzantine = GNUNET_YES; + msg->byzantine_lower_bound = htonl (opt->v.num); + break; + case GNUNET_SETU_OPTION_FORCE_FULL: + msg->force_full = GNUNET_YES; + break; + case GNUNET_SETU_OPTION_FORCE_DELTA: + msg->force_delta = GNUNET_YES; + break; + case GNUNET_SETU_OPTION_SYMMETRIC: + msg->symmetric = GNUNET_YES; + break; + default: + LOG (GNUNET_ERROR_TYPE_ERROR, + "Option with type %d not recognized\n", + (int) opt->type); + } + } oh = GNUNET_new (struct GNUNET_SETU_OperationHandle); oh->result_cb = result_cb; oh->result_cls = result_cls; -- cgit v1.2.3 From e75869506cc08e08056168383bd4ab02e1f007de Mon Sep 17 00:00:00 2001 From: Martin Schanzenbach Date: Wed, 19 Aug 2020 19:05:23 +0200 Subject: - towards separation between credentials and presentations thereof, wip, ftbfs --- src/include/gnunet_protocols.h | 12 +- src/include/gnunet_reclaim_lib.h | 394 ++++++++++++++---- src/include/gnunet_reclaim_plugin.h | 187 +++++++-- src/include/gnunet_reclaim_service.h | 61 ++- src/reclaim/Makefile.am | 18 +- src/reclaim/gnunet-reclaim.c | 216 +++++----- src/reclaim/gnunet-service-reclaim.c | 272 ++++++------- src/reclaim/gnunet-service-reclaim_tickets.c | 194 ++++----- src/reclaim/gnunet-service-reclaim_tickets.h | 2 +- src/reclaim/json_reclaim.c | 60 +-- src/reclaim/json_reclaim.h | 11 +- src/reclaim/oidc_helper.c | 110 +++--- src/reclaim/oidc_helper.h | 13 +- src/reclaim/plugin_reclaim_attestation_jwt.c | 321 --------------- src/reclaim/plugin_reclaim_credential_jwt.c | 320 +++++++++++++++ src/reclaim/plugin_rest_openid_connect.c | 142 +++---- src/reclaim/plugin_rest_reclaim.c | 144 +++---- src/reclaim/reclaim.h | 28 +- src/reclaim/reclaim_api.c | 161 ++++---- src/reclaim/reclaim_attestation.c | 570 --------------------------- src/reclaim/reclaim_attestation.h | 64 --- src/reclaim/reclaim_attribute.c | 18 +- src/reclaim/reclaim_attribute.h | 37 +- src/reclaim/reclaim_credential.c | 570 +++++++++++++++++++++++++++ src/reclaim/reclaim_credential.h | 64 +++ 25 files changed, 2171 insertions(+), 1818 deletions(-) delete mode 100644 src/reclaim/plugin_reclaim_attestation_jwt.c create mode 100644 src/reclaim/plugin_reclaim_credential_jwt.c delete mode 100644 src/reclaim/reclaim_attestation.c delete mode 100644 src/reclaim/reclaim_attestation.h create mode 100644 src/reclaim/reclaim_credential.c create mode 100644 src/reclaim/reclaim_credential.h diff --git a/src/include/gnunet_protocols.h b/src/include/gnunet_protocols.h index 4526b75d9..d9821ffe8 100644 --- a/src/include/gnunet_protocols.h +++ b/src/include/gnunet_protocols.h @@ -2900,17 +2900,17 @@ extern "C" { #define GNUNET_MESSAGE_TYPE_RECLAIM_ATTRIBUTE_DELETE 976 -#define GNUNET_MESSAGE_TYPE_RECLAIM_ATTESTATION_STORE 977 +#define GNUNET_MESSAGE_TYPE_RECLAIM_CREDENTIAL_STORE 977 -#define GNUNET_MESSAGE_TYPE_RECLAIM_ATTESTATION_DELETE 978 +#define GNUNET_MESSAGE_TYPE_RECLAIM_CREDENTIAL_DELETE 978 -#define GNUNET_MESSAGE_TYPE_RECLAIM_ATTESTATION_RESULT 979 +#define GNUNET_MESSAGE_TYPE_RECLAIM_CREDENTIAL_RESULT 979 -#define GNUNET_MESSAGE_TYPE_RECLAIM_ATTESTATION_ITERATION_START 980 +#define GNUNET_MESSAGE_TYPE_RECLAIM_CREDENTIAL_ITERATION_START 980 -#define GNUNET_MESSAGE_TYPE_RECLAIM_ATTESTATION_ITERATION_STOP 981 +#define GNUNET_MESSAGE_TYPE_RECLAIM_CREDENTIAL_ITERATION_STOP 981 -#define GNUNET_MESSAGE_TYPE_RECLAIM_ATTESTATION_ITERATION_NEXT 982 +#define GNUNET_MESSAGE_TYPE_RECLAIM_CREDENTIAL_ITERATION_NEXT 982 /************************************************** diff --git a/src/include/gnunet_reclaim_lib.h b/src/include/gnunet_reclaim_lib.h index 7d521ffa8..f5c3d3864 100644 --- a/src/include/gnunet_reclaim_lib.h +++ b/src/include/gnunet_reclaim_lib.h @@ -39,26 +39,34 @@ extern "C" { #include "gnunet_util_lib.h" +enum GNUNET_RECLAIM_AttributeType { + /** + * No value attribute. + */ + GNUNET_RECLAIM_ATTRIBUTE_TYPE_NONE = 0, -/** - * No value attribute. - */ -#define GNUNET_RECLAIM_ATTRIBUTE_TYPE_NONE 0 + /** + * String attribute. + */ + GNUNET_RECLAIM_ATTRIBUTE_TYPE_STRING = 1 +}; -/** - * String attribute. - */ -#define GNUNET_RECLAIM_ATTRIBUTE_TYPE_STRING 1 +enum GNUNET_RECLAIM_CredentialType { + /** + * No value credential. + */ + GNUNET_RECLAIM_CREDENTIAL_TYPE_NONE = 0, -/** -* No value attestation. -*/ -#define GNUNET_RECLAIM_ATTESTATION_TYPE_NONE 10 + /** + * A JSON Web Token credential. + */ + GNUNET_RECLAIM_CREDENTIAL_TYPE_JWT = 1, -/** -* A JSON Web Token attestation. -*/ -#define GNUNET_RECLAIM_ATTESTATION_TYPE_JWT 11 + /** + * libpabc credential + */ + GNUNET_RECLAIM_CREDENTIAL_TYPE_PABC = 2 +}; /** * We want an ID to be a 256-bit symmetric key @@ -107,9 +115,10 @@ struct GNUNET_RECLAIM_Attribute struct GNUNET_RECLAIM_Identifier id; /** - * Referenced ID of Attestation (may be 0 if self-attested) + * Referenced ID of credential + * (may be GNUNET_RECLAIM_ID_ZERO if self-creded) */ - struct GNUNET_RECLAIM_Identifier attestation; + struct GNUNET_RECLAIM_Identifier credential; /** * Type of Claim @@ -141,9 +150,9 @@ struct GNUNET_RECLAIM_Attribute }; /** - * An attestation. + * A credential. */ -struct GNUNET_RECLAIM_Attestation +struct GNUNET_RECLAIM_Credential { /** * ID @@ -161,7 +170,7 @@ struct GNUNET_RECLAIM_Attestation uint32_t flag; /** - * The name of the attribute. Note "name" must never be individually + * The name of the credential. Note: must never be individually * free'd */ const char *name; @@ -172,7 +181,36 @@ struct GNUNET_RECLAIM_Attestation size_t data_size; /** - * Binary value stored as attribute value. Note: "data" must never + * Binary value stored as credential value. Note: "data" must never + * be individually 'malloc'ed, but instead always points into some + * existing data area. + */ + const void *data; +}; + + +/** + * A credential presentation. + */ +struct GNUNET_RECLAIM_Presentation +{ + /** + * The credential id of which this is a presentation. + */ + struct GNUNET_RECLAIM_Identifier credential_id; + + /** + * Type/Format of Claim + */ + uint32_t type; + + /** + * Number of bytes in @e data. + */ + size_t data_size; + + /** + * Binary value stored as presentation value. Note: "data" must never * be individually 'malloc'ed, but instead always points into some * existing data area. */ @@ -180,6 +218,7 @@ struct GNUNET_RECLAIM_Attestation }; + /** * A list of GNUNET_RECLAIM_Attribute structures. */ @@ -217,56 +256,94 @@ struct GNUNET_RECLAIM_AttributeListEntry }; /** - * A list of GNUNET_RECLAIM_Attestation structures. + * A list of GNUNET_RECLAIM_Credential structures. */ -struct GNUNET_RECLAIM_AttestationList +struct GNUNET_RECLAIM_CredentialList { /** * List head */ - struct GNUNET_RECLAIM_AttestationListEntry *list_head; + struct GNUNET_RECLAIM_CredentialListEntry *list_head; /** * List tail */ - struct GNUNET_RECLAIM_AttestationListEntry *list_tail; + struct GNUNET_RECLAIM_CredentialListEntry *list_tail; }; -struct GNUNET_RECLAIM_AttestationListEntry +struct GNUNET_RECLAIM_CredentialListEntry { /** * DLL */ - struct GNUNET_RECLAIM_AttestationListEntry *prev; + struct GNUNET_RECLAIM_CredentialListEntry *prev; /** * DLL */ - struct GNUNET_RECLAIM_AttestationListEntry *next; + struct GNUNET_RECLAIM_CredentialListEntry *next; /** - * The attestation + * The credential */ - struct GNUNET_RECLAIM_Attestation *attestation; + struct GNUNET_RECLAIM_Credential *credential; }; +/** + * A list of GNUNET_RECLAIM_Presentation structures. + */ +struct GNUNET_RECLAIM_PresentationList +{ + /** + * List head + */ + struct GNUNET_RECLAIM_PresentationListEntry *list_head; + + /** + * List tail + */ + struct GNUNET_RECLAIM_PresentationListEntry *list_tail; +}; + + +struct GNUNET_RECLAIM_PresentationListEntry +{ + /** + * DLL + */ + struct GNUNET_RECLAIM_PresentationListEntry *prev; + + /** + * DLL + */ + struct GNUNET_RECLAIM_PresentationListEntry *next; + + /** + * The credential + */ + struct GNUNET_RECLAIM_Presentation *presentation; + +}; + + + /** * Create a new attribute claim. * * @param attr_name the attribute name - * @param attestation ID of the attestation (may be NULL) + * @param credential ID of the credential (may be NULL) * @param type the attribute type - * @param data the attribute value. Must be the mapped name if attestation not NULL + * @param data the attribute value. Must be #attr_name if credential not NULL * @param data_size the attribute value size * @return the new attribute */ struct GNUNET_RECLAIM_Attribute * GNUNET_RECLAIM_attribute_new (const char *attr_name, const struct - GNUNET_RECLAIM_Identifier *attestation, + GNUNET_RECLAIM_Identifier *credential, uint32_t type, const void *data, size_t data_size); @@ -298,7 +375,7 @@ GNUNET_RECLAIM_attribute_list_destroy ( * * @param attrs the attribute list to add to * @param attr_name the name of the new attribute claim - * @param attestation attestation ID (may be NULL) + * @param credential credential ID (may be NULL) * @param type the type of the claim * @param data claim payload * @param data_size claim payload size @@ -307,7 +384,7 @@ void GNUNET_RECLAIM_attribute_list_add ( struct GNUNET_RECLAIM_AttributeList *attrs, const char *attr_name, - const struct GNUNET_RECLAIM_Identifier *attestation, + const struct GNUNET_RECLAIM_Identifier *credential, uint32_t type, const void *data, size_t data_size); @@ -439,8 +516,8 @@ GNUNET_RECLAIM_attribute_number_to_typename (uint32_t type); * @return the required buffer size */ size_t -GNUNET_RECLAIM_attestation_list_serialize_get_size ( - const struct GNUNET_RECLAIM_AttestationList *attestations); +GNUNET_RECLAIM_credential_list_serialize_get_size ( + const struct GNUNET_RECLAIM_CredentialList *credentials); /** @@ -449,8 +526,8 @@ GNUNET_RECLAIM_attestation_list_serialize_get_size ( * @param attrs list to destroy */ void -GNUNET_RECLAIM_attestation_list_destroy ( - struct GNUNET_RECLAIM_AttestationList *attestations); +GNUNET_RECLAIM_credential_list_destroy ( + struct GNUNET_RECLAIM_CredentialList *credentials); /** @@ -462,8 +539,8 @@ GNUNET_RECLAIM_attestation_list_destroy ( * @param data_size claim payload size */ void -GNUNET_RECLAIM_attestation_list_add ( - struct GNUNET_RECLAIM_AttestationList *attrs, +GNUNET_RECLAIM_credential_list_add ( + struct GNUNET_RECLAIM_CredentialList *attrs, const char *att_name, uint32_t type, const void *data, @@ -478,8 +555,8 @@ GNUNET_RECLAIM_attestation_list_add ( * @return length of serialized data */ size_t -GNUNET_RECLAIM_attestation_list_serialize ( - const struct GNUNET_RECLAIM_AttestationList *attrs, +GNUNET_RECLAIM_credential_list_serialize ( + const struct GNUNET_RECLAIM_CredentialList *attrs, char *result); @@ -490,75 +567,75 @@ GNUNET_RECLAIM_attestation_list_serialize ( * @param data_size the length of the serialized data * @return a GNUNET_IDENTITY_PROVIDER_AttributeList, must be free'd by caller */ -struct GNUNET_RECLAIM_AttestationList * -GNUNET_RECLAIM_attestation_list_deserialize (const char *data, +struct GNUNET_RECLAIM_CredentialList * +GNUNET_RECLAIM_credential_list_deserialize (const char *data, size_t data_size); /** - * @param attestation the attestation to serialize + * @param credential the credential to serialize * @return the required buffer size */ size_t -GNUNET_RECLAIM_attestation_serialize_get_size ( - const struct GNUNET_RECLAIM_Attestation *attestation); +GNUNET_RECLAIM_credential_serialize_get_size ( + const struct GNUNET_RECLAIM_Credential *credential); /** - * Serialize an attestation + * Serialize an credential * - * @param attestation the attestation to serialize - * @param result the serialized attestation + * @param credential the credential to serialize + * @param result the serialized credential * @return length of serialized data */ size_t -GNUNET_RECLAIM_attestation_serialize ( - const struct GNUNET_RECLAIM_Attestation *attestation, +GNUNET_RECLAIM_credential_serialize ( + const struct GNUNET_RECLAIM_Credential *credential, char *result); /** - * Deserialize an attestation + * Deserialize an credential * - * @param data the serialized attestation + * @param data the serialized credential * @param data_size the length of the serialized data * * @return a GNUNET_IDENTITY_PROVIDER_Attribute, must be free'd by caller */ -struct GNUNET_RECLAIM_Attestation * -GNUNET_RECLAIM_attestation_deserialize (const char *data, size_t data_size); +struct GNUNET_RECLAIM_Credential * +GNUNET_RECLAIM_credential_deserialize (const char *data, size_t data_size); /** - * Create a new attestation. + * Create a new credential. * - * @param name the attestation name - * @param type the attestation type - * @param data the attestation value - * @param data_size the attestation value size - * @return the new attestation - */ -struct GNUNET_RECLAIM_Attestation * -GNUNET_RECLAIM_attestation_new (const char *name, + * @param name the credential name + * @param type the credential type + * @param data the credential value + * @param data_size the credential value size + * @return the new credential + */ +struct GNUNET_RECLAIM_Credential * +GNUNET_RECLAIM_credential_new (const char *name, uint32_t type, const void *data, size_t data_size); /** - * Convert the 'claim' of an attestation to a string + * Convert the 'claim' of an credential to a string * - * @param type the type of attestation + * @param type the type of credential * @param data claim in binary encoding * @param data_size number of bytes in @a data * @return NULL on error, otherwise human-readable representation of the claim */ char * -GNUNET_RECLAIM_attestation_value_to_string (uint32_t type, +GNUNET_RECLAIM_credential_value_to_string (uint32_t type, const void *data, size_t data_size); /** - * Convert human-readable version of a 'claim' of an attestation to the binary + * Convert human-readable version of a 'claim' of an credential to the binary * representation * * @param type type of the claim @@ -568,48 +645,199 @@ GNUNET_RECLAIM_attestation_value_to_string (uint32_t type, * @return #GNUNET_OK on success */ int -GNUNET_RECLAIM_attestation_string_to_value (uint32_t type, +GNUNET_RECLAIM_credential_string_to_value (uint32_t type, const char *s, void **data, size_t *data_size); /** - * Convert an attestation type number to the corresponding attestation type string + * Convert an credential type number to the corresponding credential type string * * @param type number of a type * @return corresponding typestring, NULL on error */ const char * -GNUNET_RECLAIM_attestation_number_to_typename (uint32_t type); +GNUNET_RECLAIM_credential_number_to_typename (uint32_t type); /** - * Convert an attestation type name to the corresponding number + * Convert an credential type name to the corresponding number * * @param typename name to convert * @return corresponding number, UINT32_MAX on error */ uint32_t -GNUNET_RECLAIM_attestation_typename_to_number (const char *typename); +GNUNET_RECLAIM_credential_typename_to_number (const char *typename); /** - * Convert an attestation type name to the corresponding number + * Convert an credential type name to the corresponding number * * @param typename name to convert * @return corresponding number, UINT32_MAX on error */ struct GNUNET_RECLAIM_AttributeList* -GNUNET_RECLAIM_attestation_get_attributes (const struct - GNUNET_RECLAIM_Attestation *attest); +GNUNET_RECLAIM_credential_get_attributes (const struct + GNUNET_RECLAIM_Credential *cred); char* -GNUNET_RECLAIM_attestation_get_issuer (const struct - GNUNET_RECLAIM_Attestation *attest); +GNUNET_RECLAIM_credential_get_issuer (const struct + GNUNET_RECLAIM_Credential *cred); int -GNUNET_RECLAIM_attestation_get_expiration (const struct - GNUNET_RECLAIM_Attestation *attest, +GNUNET_RECLAIM_credential_get_expiration (const struct + GNUNET_RECLAIM_Credential *cred, struct GNUNET_TIME_Absolute *exp); +/** + * Get required size for serialization buffer + * + * @param presentations the presentation list to serialize + * @return the required buffer size + */ +size_t +GNUNET_RECLAIM_presentation_list_serialize_get_size ( + const struct GNUNET_RECLAIM_PresentationList *presentations); + + +/** + * Destroy presentations list + * + * @param presentations list to destroy + */ +void +GNUNET_RECLAIM_presentation_list_destroy ( + struct GNUNET_RECLAIM_PresentationList *presentations); + + +/** + * Serialize a presentation list + * + * @param presentations the attribute list to serialize + * @param result the serialized list + * @return length of serialized data + */ +size_t +GNUNET_RECLAIM_presentation_list_serialize ( + const struct GNUNET_RECLAIM_PresentationList *presentations, + char *result); + + +/** + * Deserialize a presentation list + * + * @param data the serialized list + * @param data_size the length of the serialized data + * @return a GNUNET_RECLAIM_PresentationList, must be free'd by caller + */ +struct GNUNET_RECLAIM_PresentationList * +GNUNET_RECLAIM_presentation_list_deserialize (const char *data, + size_t data_size); + + +/** + * @param presentation the presentation to serialize + * @return the required buffer size + */ +size_t +GNUNET_RECLAIM_presentation_serialize_get_size ( + const struct GNUNET_RECLAIM_Presentation *presentation); + + +/** + * Serialize a presentation. + * + * @param presentation the presentation to serialize + * @param result the serialized presentation + * @return length of serialized data + */ +size_t +GNUNET_RECLAIM_presentation_serialize ( + const struct GNUNET_RECLAIM_Presentation *presentation, + char *result); + + +/** + * Deserialize a presentation + * + * @param data the serialized presentation + * @param data_size the length of the serialized data + * + * @return a GNUNET_RECLAIM_Presentation, must be free'd by caller + */ +struct GNUNET_RECLAIM_Presentation * +GNUNET_RECLAIM_presentation_deserialize (const char *data, size_t data_size); + + +/** + * Convert the 'claim' of a presentation to a string + * + * @param type the type of presentation + * @param data presentation in binary encoding + * @param data_size number of bytes in @a data + * @return NULL on error, otherwise human-readable representation of the claim + */ +char * +GNUNET_RECLAIM_presentation_value_to_string (uint32_t type, + const void *data, + size_t data_size); + +/** + * Convert human-readable version of a 'claim' of a presentation to the binary + * representation + * + * @param type type of the presentation + * @param s human-readable string + * @param data set to value in binary encoding (will be allocated) + * @param data_size set to number of bytes in @a data + * @return #GNUNET_OK on success + */ +int +GNUNET_RECLAIM_presentation_string_to_value (uint32_t type, + const char *s, + void **data, + size_t *data_size); + +/** + * Convert a presentation type number to the corresponding credential type + * string. + * + * @param type number of a type + * @return corresponding typestring, NULL on error + */ +const char * +GNUNET_RECLAIM_presentation_number_to_typename (uint32_t type); + +struct GNUNET_RECLAIM_AttributeList* +GNUNET_RECLAIM_presentation_get_attributes (const struct + GNUNET_RECLAIM_Presentation *cred); + +char* +GNUNET_RECLAIM_presentation_get_issuer (const struct + GNUNET_RECLAIM_Presentation *cred); + +int +GNUNET_RECLAIM_presentation_get_expiration (const struct + GNUNET_RECLAIM_Presentation *cred, + struct GNUNET_TIME_Absolute *exp); + + + +/** + * Create a presentation from a credential and a lift of (selected) + * attributes in the credential. + * FIXME not yet implemented + * + * @param cred the credential to use + * @param attrs the attributes to present from the credential + * @return the credential presentation presenting the attributes according + * to the presentation mechanism of the credential + * or NULL on error. + */ +struct GNUNET_RECLAIM_Presentation* +GNUNET_RECLAIM_credential_get_presentation ( + const struct GNUNET_RECLAIM_Credential *cred, + const struct GNUNET_RECLAIM_AttributeList *attrs); + + #if 0 /* keep Emacsens' auto-indent happy */ { #endif diff --git a/src/include/gnunet_reclaim_plugin.h b/src/include/gnunet_reclaim_plugin.h index 7ee9e730f..af6c74e0b 100644 --- a/src/include/gnunet_reclaim_plugin.h +++ b/src/include/gnunet_reclaim_plugin.h @@ -27,8 +27,8 @@ * @defgroup reclaim-attribute-plugin reclaim plugin API for attributes/claims * @{ */ -#ifndef GNUNET_RECLAIM_AttributePLUGIN_H -#define GNUNET_RECLAIM_AttributePLUGIN_H +#ifndef GNUNET_RECLAIM_PLUGIN_H +#define GNUNET_RECLAIM_PLUGIN_H #include "gnunet_util_lib.h" #include "gnunet_reclaim_lib.h" @@ -113,7 +113,7 @@ typedef const char *(*GNUNET_RECLAIM_AttributeNumberToTypenameFunction) ( * @param data_size number of bytes in @a data * @return NULL on error, otherwise human-readable representation of the value */ -typedef char *(*GNUNET_RECLAIM_AttestationValueToStringFunction) ( +typedef char *(*GNUNET_RECLAIM_CredentialValueToStringFunction) ( void *cls, uint32_t type, const void *data, @@ -132,7 +132,7 @@ typedef char *(*GNUNET_RECLAIM_AttestationValueToStringFunction) ( * @param data_size set to number of bytes in @a data * @return #GNUNET_OK on success */ -typedef int (*GNUNET_RECLAIM_AttestationStringToValueFunction) ( +typedef int (*GNUNET_RECLAIM_CredentialStringToValueFunction) ( void *cls, uint32_t type, const char *s, @@ -148,7 +148,7 @@ typedef int (*GNUNET_RECLAIM_AttestationStringToValueFunction) ( * @param typename name to convert * @return corresponding number, UINT32_MAX on error */ -typedef uint32_t (*GNUNET_RECLAIM_AttestationTypenameToNumberFunction) ( +typedef uint32_t (*GNUNET_RECLAIM_CredentialTypenameToNumberFunction) ( void *cls, const char *typename); @@ -161,45 +161,144 @@ typedef uint32_t (*GNUNET_RECLAIM_AttestationTypenameToNumberFunction) ( * @param type number of a type to convert * @return corresponding typestring, NULL on error */ -typedef const char *(*GNUNET_RECLAIM_AttestationNumberToTypenameFunction) ( +typedef const char *(*GNUNET_RECLAIM_CredentialNumberToTypenameFunction) ( void *cls, uint32_t type); /** - * Function called to extract attributes from an attestation + * Function called to extract attributes from a credential * * @param cls closure - * @param attest the attestation object + * @param cred the credential object * @return an attribute list */ typedef struct GNUNET_RECLAIM_AttributeList *(* -GNUNET_RECLAIM_AttestationGetAttributesFunction) ( +GNUNET_RECLAIM_CredentialGetAttributesFunction) ( void *cls, - const struct GNUNET_RECLAIM_Attestation *attest); + const struct GNUNET_RECLAIM_Credential *cred); /** - * Function called to get the issuer of the attestation (as string) + * Function called to get the issuer of the credential (as string) * * @param cls closure - * @param attest the attestation object + * @param cred the credential object * @return corresponding issuer string */ -typedef char *(*GNUNET_RECLAIM_AttestationGetIssuerFunction) ( +typedef char *(*GNUNET_RECLAIM_CredentialGetIssuerFunction) ( void *cls, - const struct GNUNET_RECLAIM_Attestation *attest); + const struct GNUNET_RECLAIM_Credential *cred); /** - * Function called to get the expiration of the attestation + * Function called to get the expiration of the credential * * @param cls closure - * @param attest the attestation object + * @param cred the credential object * @param where to write the value * @return GNUNET_OK if successful */ -typedef int (*GNUNET_RECLAIM_AttestationGetExpirationFunction) ( +typedef int (*GNUNET_RECLAIM_CredentialGetExpirationFunction) ( void *cls, - const struct GNUNET_RECLAIM_Attestation *attest, + const struct GNUNET_RECLAIM_Credential *cred, + struct GNUNET_TIME_Absolute *expiration); + +/** + * Function called to convert the binary value @a data of an attribute of + * type @a type to a human-readable string. + * + * @param cls closure + * @param type type of the attribute + * @param data value in binary encoding + * @param data_size number of bytes in @a data + * @return NULL on error, otherwise human-readable representation of the value + */ +typedef char *(*GNUNET_RECLAIM_PresentationValueToStringFunction) ( + void *cls, + uint32_t type, + const void *data, + size_t data_size); + + +/** + * Function called to convert human-readable version of the value @a s + * of an attribute of type @a type to the respective binary + * representation. + * + * @param cls closure + * @param type type of the attribute + * @param s human-readable string + * @param data set to value in binary encoding (will be allocated) + * @param data_size set to number of bytes in @a data + * @return #GNUNET_OK on success + */ +typedef int (*GNUNET_RECLAIM_PresentationStringToValueFunction) ( + void *cls, + uint32_t type, + const char *s, + void **data, + size_t *data_size); + + +/** + * Function called to convert a type name to the + * corresponding number. + * + * @param cls closure + * @param typename name to convert + * @return corresponding number, UINT32_MAX on error + */ +typedef uint32_t (*GNUNET_RECLAIM_PresentationTypenameToNumberFunction) ( + void *cls, + const char *typename); + + +/** + * Function called to convert a type number (i.e. 1) to the + * corresponding type string + * + * @param cls closure + * @param type number of a type to convert + * @return corresponding typestring, NULL on error + */ +typedef const char *(*GNUNET_RECLAIM_PresentationNumberToTypenameFunction) ( + void *cls, + uint32_t type); + +/** + * Function called to extract attributes from a credential + * + * @param cls closure + * @param cred the credential object + * @return an attribute list + */ +typedef struct + GNUNET_RECLAIM_AttributeList *(* +GNUNET_RECLAIM_PresentationGetAttributesFunction) ( + void *cls, + const struct GNUNET_RECLAIM_Presentation *cred); + +/** + * Function called to get the issuer of the credential (as string) + * + * @param cls closure + * @param cred the credential object + * @return corresponding issuer string + */ +typedef char *(*GNUNET_RECLAIM_PresentationGetIssuerFunction) ( + void *cls, + const struct GNUNET_RECLAIM_Presentation *cred); + +/** + * Function called to get the expiration of the credential + * + * @param cls closure + * @param cred the credential object + * @param where to write the value + * @return GNUNET_OK if successful + */ +typedef int (*GNUNET_RECLAIM_PresentationGetExpirationFunction) ( + void *cls, + const struct GNUNET_RECLAIM_Presentation *cred, struct GNUNET_TIME_Absolute *expiration); @@ -240,7 +339,7 @@ struct GNUNET_RECLAIM_AttributePluginFunctions * Each plugin is required to return a pointer to a struct of this * type as the return value from its entry point. */ -struct GNUNET_RECLAIM_AttestationPluginFunctions +struct GNUNET_RECLAIM_CredentialPluginFunctions { /** * Closure for all of the callbacks. @@ -250,37 +349,73 @@ struct GNUNET_RECLAIM_AttestationPluginFunctions /** * Conversion to string. */ - GNUNET_RECLAIM_AttestationValueToStringFunction value_to_string; + GNUNET_RECLAIM_CredentialValueToStringFunction value_to_string; + + /** + * Conversion to binary. + */ + GNUNET_RECLAIM_CredentialStringToValueFunction string_to_value; + + /** + * Typename to number. + */ + GNUNET_RECLAIM_CredentialTypenameToNumberFunction typename_to_number; + + /** + * Number to typename. + */ + GNUNET_RECLAIM_CredentialNumberToTypenameFunction number_to_typename; + + /** + * Attesation attributes. + */ + GNUNET_RECLAIM_CredentialGetAttributesFunction get_attributes; + + /** + * Attesation issuer. + */ + GNUNET_RECLAIM_CredentialGetIssuerFunction get_issuer; + + /** + * Expiration. + */ + GNUNET_RECLAIM_CredentialGetExpirationFunction get_expiration; + + /** + * Conversion to string. + */ + GNUNET_RECLAIM_PresentationValueToStringFunction value_to_string_p; /** * Conversion to binary. */ - GNUNET_RECLAIM_AttestationStringToValueFunction string_to_value; + GNUNET_RECLAIM_PresentationStringToValueFunction string_to_value_p; /** * Typename to number. */ - GNUNET_RECLAIM_AttestationTypenameToNumberFunction typename_to_number; + GNUNET_RECLAIM_PresentationTypenameToNumberFunction typename_to_number_p; /** * Number to typename. */ - GNUNET_RECLAIM_AttestationNumberToTypenameFunction number_to_typename; + GNUNET_RECLAIM_PresentationNumberToTypenameFunction number_to_typename_p; /** * Attesation attributes. */ - GNUNET_RECLAIM_AttestationGetAttributesFunction get_attributes; + GNUNET_RECLAIM_PresentationGetAttributesFunction get_attributes_p; /** * Attesation issuer. */ - GNUNET_RECLAIM_AttestationGetIssuerFunction get_issuer; + GNUNET_RECLAIM_PresentationGetIssuerFunction get_issuer_p; /** * Expiration. */ - GNUNET_RECLAIM_AttestationGetExpirationFunction get_expiration; + GNUNET_RECLAIM_PresentationGetExpirationFunction get_expiration_p; + }; diff --git a/src/include/gnunet_reclaim_service.h b/src/include/gnunet_reclaim_service.h index 139c44ae7..8387c79b0 100644 --- a/src/include/gnunet_reclaim_service.h +++ b/src/include/gnunet_reclaim_service.h @@ -113,7 +113,6 @@ typedef void (*GNUNET_RECLAIM_ContinuationWithStatus) (void *cls, * @param cls The callback closure * @param identity The identity authoritative over the attributes * @param attr The attribute - * @param attestation The attestation for the attribute (may be NULL) */ typedef void (*GNUNET_RECLAIM_AttributeResult) ( void *cls, const struct GNUNET_CRYPTO_EcdsaPublicKey *identity, @@ -125,25 +124,25 @@ typedef void (*GNUNET_RECLAIM_AttributeResult) ( * @param cls The callback closure * @param identity The identity authoritative over the attributes * @param attr The attribute - * @param attestation The attestation for the attribute (may be NULL) + * @param presentation The presentation for the credential (may be NULL) */ typedef void (*GNUNET_RECLAIM_AttributeTicketResult) ( void *cls, const struct GNUNET_CRYPTO_EcdsaPublicKey *identity, const struct GNUNET_RECLAIM_Attribute *attr, - const struct GNUNET_RECLAIM_Attestation *attestation); + const struct GNUNET_RECLAIM_Presentation *presentation); /** - * Callback used to notify the client of attestation results. + * Callback used to notify the client of credential results. * * @param cls The callback closure * @param identity The identity authoritative over the attributes - * @param attestation The attestation + * @param credential The credential * @param attributes the parsed attributes */ -typedef void (*GNUNET_RECLAIM_AttestationResult) ( +typedef void (*GNUNET_RECLAIM_CredentialResult) ( void *cls, const struct GNUNET_CRYPTO_EcdsaPublicKey *identity, - const struct GNUNET_RECLAIM_Attestation *attestation); + const struct GNUNET_RECLAIM_Credential *credential); /** @@ -178,22 +177,22 @@ GNUNET_RECLAIM_attribute_store ( /** - * Store an attestation. If the attestation is already present, - * it is replaced with the new attestation. + * Store a credential. If the credential is already present, + * it is replaced with the new credential. * * @param h handle to the re:claimID service * @param pkey private key of the identity - * @param attr the attestation value - * @param exp_interval the relative expiration interval for the attestation + * @param attr the credential value + * @param exp_interval the relative expiration interval for the credential * @param cont continuation to call when done * @param cont_cls closure for @a cont * @return handle to abort the request */ struct GNUNET_RECLAIM_Operation * -GNUNET_RECLAIM_attestation_store ( +GNUNET_RECLAIM_credential_store ( struct GNUNET_RECLAIM_Handle *h, const struct GNUNET_CRYPTO_EcdsaPrivateKey *pkey, - const struct GNUNET_RECLAIM_Attestation *attestation, + const struct GNUNET_RECLAIM_Credential *credential, const struct GNUNET_TIME_Relative *exp_interval, GNUNET_RECLAIM_ContinuationWithStatus cont, void *cont_cls); @@ -218,21 +217,21 @@ GNUNET_RECLAIM_attribute_delete ( GNUNET_RECLAIM_ContinuationWithStatus cont, void *cont_cls); /** - * Delete an attestation. Tickets used to share this attestation are updated - * accordingly. + * Delete a credential. Tickets used to share use a presentation of this + * credential are updated accordingly. * * @param h handle to the re:claimID service * @param pkey Private key of the identity to add an attribute to - * @param attr The attestation + * @param cred The credential * @param cont Continuation to call when done * @param cont_cls Closure for @a cont * @return handle Used to to abort the request */ struct GNUNET_RECLAIM_Operation * -GNUNET_RECLAIM_attestation_delete ( +GNUNET_RECLAIM_credential_delete ( struct GNUNET_RECLAIM_Handle *h, const struct GNUNET_CRYPTO_EcdsaPrivateKey *pkey, - const struct GNUNET_RECLAIM_Attestation *attr, + const struct GNUNET_RECLAIM_Credential *cred, GNUNET_RECLAIM_ContinuationWithStatus cont, void *cont_cls); @@ -293,12 +292,12 @@ GNUNET_RECLAIM_get_attributes_stop ( /** - * List all attestations for a local identity. + * List all credentials for a local identity. * This MUST lock the `struct GNUNET_RECLAIM_Handle` - * for any other calls than #GNUNET_RECLAIM_get_attestations_next() and - * #GNUNET_RECLAIM_get_attestations_stop. @a proc will be called once + * for any other calls than #GNUNET_RECLAIM_get_credentials_next() and + * #GNUNET_RECLAIM_get_credentials_stop. @a proc will be called once * immediately, and then again after - * #GNUNET_RECLAIM_get_attestations_next() is invoked. + * #GNUNET_RECLAIM_get_credentials_next() is invoked. * * On error (disconnect), @a error_cb will be invoked. * On normal completion, @a finish_cb proc will be @@ -309,34 +308,34 @@ GNUNET_RECLAIM_get_attributes_stop ( * @param error_cb Function to call on error (i.e. disconnect), * the handle is afterwards invalid * @param error_cb_cls Closure for @a error_cb - * @param proc Function to call on each attestation + * @param proc Function to call on each credential * @param proc_cls Closure for @a proc * @param finish_cb Function to call on completion * the handle is afterwards invalid * @param finish_cb_cls Closure for @a finish_cb * @return an iterator Handle to use for iteration */ -struct GNUNET_RECLAIM_AttestationIterator * -GNUNET_RECLAIM_get_attestations_start ( +struct GNUNET_RECLAIM_CredentialIterator * +GNUNET_RECLAIM_get_credentials_start ( struct GNUNET_RECLAIM_Handle *h, const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity, GNUNET_SCHEDULER_TaskCallback error_cb, void *error_cb_cls, - GNUNET_RECLAIM_AttestationResult proc, + GNUNET_RECLAIM_CredentialResult proc, void *proc_cls, GNUNET_SCHEDULER_TaskCallback finish_cb, void *finish_cb_cls); /** - * Calls the record processor specified in #GNUNET_RECLAIM_get_attestation_start + * Calls the record processor specified in #GNUNET_RECLAIM_get_credentials_start * for the next record. * * @param it the iterator */ void -GNUNET_RECLAIM_get_attestations_next (struct - GNUNET_RECLAIM_AttestationIterator *ait); +GNUNET_RECLAIM_get_credentials_next ( + struct GNUNET_RECLAIM_CredentialIterator *ait); /** @@ -347,8 +346,8 @@ GNUNET_RECLAIM_get_attestations_next (struct * @param it the iterator */ void -GNUNET_RECLAIM_get_attestations_stop (struct - GNUNET_RECLAIM_AttestationIterator *ait); +GNUNET_RECLAIM_get_credentials_stop ( + struct GNUNET_RECLAIM_CredentialIterator *ait); /** diff --git a/src/reclaim/Makefile.am b/src/reclaim/Makefile.am index 9b75c11aa..a58127abf 100644 --- a/src/reclaim/Makefile.am +++ b/src/reclaim/Makefile.am @@ -13,8 +13,8 @@ REST_PLUGIN = \ libgnunet_plugin_rest_openid_connect.la \ libgnunet_plugin_rest_reclaim.la -ATTESTATION_PLUGIN = \ - libgnunet_plugin_reclaim_attestation_jwt.la +CREDENTIAL_PLUGIN = \ + libgnunet_plugin_reclaim_credential_jwt.la EXTRA_DIST = \ reclaim.conf \ @@ -34,7 +34,7 @@ lib_LTLIBRARIES = \ plugin_LTLIBRARIES = \ libgnunet_plugin_gnsrecord_reclaim.la \ libgnunet_plugin_reclaim_attribute_basic.la \ - $(ATTESTATION_PLUGIN) \ + $(CREDENTIAL_PLUGIN) \ $(REST_PLUGIN) bin_PROGRAMS = \ @@ -115,8 +115,8 @@ libgnunetreclaim_la_SOURCES = \ reclaim.h \ reclaim_attribute.c \ reclaim_attribute.h \ - reclaim_attestation.c \ - reclaim_attestation.h + reclaim_credential.c \ + reclaim_credential.h libgnunetreclaim_la_LIBADD = \ $(top_builddir)/src/util/libgnunetutil.la \ $(GN_LIBINTL) $(XLIB) @@ -133,14 +133,14 @@ libgnunet_plugin_reclaim_attribute_basic_la_LIBADD = \ libgnunet_plugin_reclaim_attribute_basic_la_LDFLAGS = \ $(GN_PLUGIN_LDFLAGS) -libgnunet_plugin_reclaim_attestation_jwt_la_SOURCES = \ - plugin_reclaim_attestation_jwt.c -libgnunet_plugin_reclaim_attestation_jwt_la_LIBADD = \ +libgnunet_plugin_reclaim_credential_jwt_la_SOURCES = \ + plugin_reclaim_credential_jwt.c +libgnunet_plugin_reclaim_credential_jwt_la_LIBADD = \ $(top_builddir)/src/util/libgnunetutil.la \ libgnunetreclaim.la \ -ljansson\ $(LTLIBINTL) -libgnunet_plugin_reclaim_attestation_jwt_la_LDFLAGS = \ +libgnunet_plugin_reclaim_credential_jwt_la_LDFLAGS = \ $(GN_PLUGIN_LDFLAGS) gnunet_reclaim_SOURCES = \ diff --git a/src/reclaim/gnunet-reclaim.c b/src/reclaim/gnunet-reclaim.c index 6bef5b6fe..a59053f5f 100644 --- a/src/reclaim/gnunet-reclaim.c +++ b/src/reclaim/gnunet-reclaim.c @@ -43,29 +43,34 @@ static int ret; static int list; /** - * List attestations flag + * List credentials flag */ -static int list_attestations; +static int list_credentials; /** - * Attestation ID string + * Credential ID string */ -static char *attestation_id; +static char *credential_id; /** - * Attestation ID + * Credential ID */ -static struct GNUNET_RECLAIM_Identifier attestation; +static struct GNUNET_RECLAIM_Identifier credential; /** - * Attestation name + * Credential name */ -static char *attestation_name; +static char *credential_name; /** - * Attestation exists + * Credential type */ -static int attestation_exists; +static char *credential_type; + +/** + * Credential exists + */ +static int credential_exists; /** * Relying party @@ -133,9 +138,9 @@ static struct GNUNET_RECLAIM_Operation *reclaim_op; static struct GNUNET_RECLAIM_AttributeIterator *attr_iterator; /** - * Attestation iterator + * Credential iterator */ -static struct GNUNET_RECLAIM_AttestationIterator *attest_iterator; +static struct GNUNET_RECLAIM_CredentialIterator *cred_iterator; /** @@ -143,10 +148,6 @@ static struct GNUNET_RECLAIM_AttestationIterator *attest_iterator; */ static struct GNUNET_RECLAIM_TicketIterator *ticket_iterator; -/** - * Master ABE key - */ -static struct GNUNET_CRYPTO_AbeMasterKey *abe_key; /** * ego private key @@ -208,20 +209,20 @@ do_cleanup (void *cls) GNUNET_RECLAIM_cancel (reclaim_op); if (NULL != attr_iterator) GNUNET_RECLAIM_get_attributes_stop (attr_iterator); - if (NULL != attest_iterator) - GNUNET_RECLAIM_get_attestations_stop (attest_iterator); + if (NULL != cred_iterator) + GNUNET_RECLAIM_get_credentials_stop (cred_iterator); if (NULL != ticket_iterator) GNUNET_RECLAIM_ticket_iteration_stop (ticket_iterator); if (NULL != reclaim_handle) GNUNET_RECLAIM_disconnect (reclaim_handle); if (NULL != identity_handle) GNUNET_IDENTITY_disconnect (identity_handle); - if (NULL != abe_key) - GNUNET_free (abe_key); if (NULL != attr_list) GNUNET_free (attr_list); if (NULL != attr_to_delete) GNUNET_free (attr_to_delete); + if (NULL == credential_type) + GNUNET_free (credential_type); } @@ -260,7 +261,7 @@ static void process_attrs (void *cls, const struct GNUNET_CRYPTO_EcdsaPublicKey *identity, const struct GNUNET_RECLAIM_Attribute *attr, - const struct GNUNET_RECLAIM_Attestation *attest) + const struct GNUNET_RECLAIM_Presentation *presentation) { char *value_str; char *id; @@ -280,7 +281,7 @@ process_attrs (void *cls, attr_type = GNUNET_RECLAIM_attribute_number_to_typename (attr->type); id = GNUNET_STRINGS_data_to_string_alloc (&attr->id, sizeof(attr->id)); value_str = NULL; - if (NULL == attest) + if (NULL == presentation) { value_str = GNUNET_RECLAIM_attribute_value_to_string (attr->type, attr->data, @@ -290,7 +291,7 @@ process_attrs (void *cls, { struct GNUNET_RECLAIM_AttributeListEntry *ale; struct GNUNET_RECLAIM_AttributeList *al - = GNUNET_RECLAIM_attestation_get_attributes (attest); + = GNUNET_RECLAIM_presentation_get_attributes (presentation); for (ale = al->list_head; NULL != ale; ale = ale->next) { @@ -298,10 +299,8 @@ process_attrs (void *cls, continue; value_str = GNUNET_RECLAIM_attribute_value_to_string (ale->attribute->type, - ale->attribute-> - data, - ale->attribute-> - data_size); + ale->attribute->data, + ale->attribute->data_size); break; } } @@ -312,7 +311,7 @@ process_attrs (void *cls, attr_type, attr->flag, id, - (NULL == attest) ? "" : "(ATTESTED)"); + (NULL == presentation) ? "" : "(ATTESTED)"); GNUNET_free (value_str); GNUNET_free (id); } @@ -362,7 +361,7 @@ static void iter_error (void *cls) { attr_iterator = NULL; - attest_iterator = NULL; + cred_iterator = NULL; fprintf (stderr, "Failed\n"); cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL); @@ -489,9 +488,9 @@ iter_finished (void *cls) claim = GNUNET_RECLAIM_attribute_new (attr_name, NULL, type, data, data_size); } - if (NULL != attestation_id) + if (NULL != credential_id) { - claim->attestation = attestation; + claim->credential = credential; } reclaim_op = GNUNET_RECLAIM_attribute_store (reclaim_handle, pkey, @@ -524,7 +523,7 @@ iter_cb (void *cls, if (0 == strcasecmp (attr_name, attr->name)) { claim = GNUNET_RECLAIM_attribute_new (attr->name, - &attr->attestation, + &attr->credential, attr->type, attr->data, attr->data_size); @@ -543,7 +542,7 @@ iter_cb (void *cls, } le = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry); le->attribute = GNUNET_RECLAIM_attribute_new (attr->name, - &attr->attestation, + &attr->credential, attr->type, attr->data, attr->data_size); @@ -562,7 +561,7 @@ iter_cb (void *cls, if (0 == strcasecmp (attr_delete, label)) { attr_to_delete = GNUNET_RECLAIM_attribute_new (attr->name, - &attr->attestation, + &attr->credential, attr->type, attr->data, attr->data_size); @@ -577,7 +576,7 @@ iter_cb (void *cls, attr->data_size); attr_type = GNUNET_RECLAIM_attribute_number_to_typename (attr->type); id = GNUNET_STRINGS_data_to_string_alloc (&attr->id, sizeof(attr->id)); - if (GNUNET_YES == GNUNET_RECLAIM_id_is_zero (&attr->attestation)) + if (GNUNET_YES == GNUNET_RECLAIM_id_is_zero (&attr->credential)) { fprintf (stdout, "%s: ``%s'' (%s); ID: %s\n", @@ -588,17 +587,17 @@ iter_cb (void *cls, } else { - char *attest_id = - GNUNET_STRINGS_data_to_string_alloc (&attr->attestation, - sizeof(attr->attestation)); + char *cred_id = + GNUNET_STRINGS_data_to_string_alloc (&attr->credential, + sizeof(attr->credential)); fprintf (stdout, - "%s: <``%s'' in attestation %s> (%s); ID: %s\n", + "%s: ``%s'' in credential presentation `%s' (%s); ID: %s\n", attr->name, attr_str, - attest_id, + cred_id, attr_type, id); - GNUNET_free (attest_id); + GNUNET_free (cred_id); } GNUNET_free (id); @@ -608,29 +607,31 @@ iter_cb (void *cls, static void -attest_iter_finished (void *cls) +cred_iter_finished (void *cls) { - attest_iterator = NULL; + cred_iterator = NULL; - // Add new attestation - if ((NULL != attestation_name) && + // Add new credential + if ((NULL != credential_name) && (NULL != attr_value)) { - struct GNUNET_RECLAIM_Attestation *attestation = - GNUNET_RECLAIM_attestation_new (attestation_name, - GNUNET_RECLAIM_ATTESTATION_TYPE_JWT, // FIXME hardcoded - attr_value, - strlen (attr_value)); - reclaim_op = GNUNET_RECLAIM_attestation_store (reclaim_handle, - pkey, - attestation, - &exp_interval, - store_cont, - NULL); + enum GNUNET_RECLAIM_CredentialType ctype = + GNUNET_RECLAIM_credential_typename_to_number (credential_type); + struct GNUNET_RECLAIM_Credential *credential = + GNUNET_RECLAIM_credential_new (credential_name, + ctype, + attr_value, + strlen (attr_value)); + reclaim_op = GNUNET_RECLAIM_credential_store (reclaim_handle, + pkey, + credential, + &exp_interval, + store_cont, + NULL); return; } - if (list_attestations) + if (list_credentials) { cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL); return; @@ -648,34 +649,34 @@ attest_iter_finished (void *cls) static void -attest_iter_cb (void *cls, - const struct GNUNET_CRYPTO_EcdsaPublicKey *identity, - const struct GNUNET_RECLAIM_Attestation *attest) +cred_iter_cb (void *cls, + const struct GNUNET_CRYPTO_EcdsaPublicKey *identity, + const struct GNUNET_RECLAIM_Credential *cred) { - char *attest_str; + char *cred_str; char *attr_str; char *id; - const char *attest_type; + const char *cred_type; struct GNUNET_RECLAIM_AttributeListEntry *ale; - if (GNUNET_YES == GNUNET_RECLAIM_id_is_equal (&attestation, - &attest->id)) - attestation_exists = GNUNET_YES; - if (list_attestations) + if (GNUNET_YES == GNUNET_RECLAIM_id_is_equal (&credential, + &cred->id)) + credential_exists = GNUNET_YES; + if (list_credentials) { - attest_str = GNUNET_RECLAIM_attestation_value_to_string (attest->type, - attest->data, - attest->data_size); - attest_type = GNUNET_RECLAIM_attestation_number_to_typename (attest->type); - id = GNUNET_STRINGS_data_to_string_alloc (&attest->id, sizeof(attest->id)); + cred_str = GNUNET_RECLAIM_credential_value_to_string (cred->type, + cred->data, + cred->data_size); + cred_type = GNUNET_RECLAIM_credential_number_to_typename (cred->type); + id = GNUNET_STRINGS_data_to_string_alloc (&cred->id, sizeof(cred->id)); fprintf (stdout, "%s: ``%s'' (%s); ID: %s\n", - attest->name, - attest_str, - attest_type, + cred->name, + cred_str, + cred_type, id); struct GNUNET_RECLAIM_AttributeList *attrs = - GNUNET_RECLAIM_attestation_get_attributes (attest); + GNUNET_RECLAIM_credential_get_attributes (cred); if (NULL != attrs) { fprintf (stdout, @@ -684,11 +685,8 @@ attest_iter_cb (void *cls, { attr_str = GNUNET_RECLAIM_attribute_value_to_string ( ale->attribute->type, - ale->attribute-> - data, - ale->attribute-> - data_size); - + ale->attribute->data, + ale->attribute->data_size); fprintf (stdout, "\t %s: %s\n", ale->attribute->name, attr_str); GNUNET_free (attr_str); @@ -697,7 +695,7 @@ attest_iter_cb (void *cls, } GNUNET_free (id); } - GNUNET_RECLAIM_get_attestations_next (attest_iterator); + GNUNET_RECLAIM_get_credentials_next (cred_iterator); } @@ -710,12 +708,14 @@ start_process () cleanup_task = GNUNET_SCHEDULER_add_now (&do_cleanup, NULL); return; } - attestation = GNUNET_RECLAIM_ID_ZERO; - if (NULL != attestation_id) - GNUNET_STRINGS_string_to_data (attestation_id, - strlen (attestation_id), - &attestation, sizeof(attestation)); - attestation_exists = GNUNET_NO; + if (NULL == credential_type) + credential_type = GNUNET_strdup ("JWT"); + credential = GNUNET_RECLAIM_ID_ZERO; + if (NULL != credential_id) + GNUNET_STRINGS_string_to_data (credential_id, + strlen (credential_id), + &credential, sizeof(credential)); + credential_exists = GNUNET_NO; if (list_tickets) { ticket_iterator = GNUNET_RECLAIM_ticket_iteration_start (reclaim_handle, @@ -750,15 +750,14 @@ start_process () attr_list = GNUNET_new (struct GNUNET_RECLAIM_AttributeList); claim = NULL; - attest_iterator = GNUNET_RECLAIM_get_attestations_start (reclaim_handle, - pkey, - &iter_error, - NULL, - &attest_iter_cb, - NULL, - & - attest_iter_finished, - NULL); + cred_iterator = GNUNET_RECLAIM_get_credentials_start (reclaim_handle, + pkey, + &iter_error, + NULL, + &cred_iter_cb, + NULL, + &cred_iter_finished, + NULL); } @@ -856,20 +855,20 @@ main (int argc, char *const argv[]) gettext_noop ("List attributes for EGO"), &list), GNUNET_GETOPT_option_flag ('A', - "attestations", - gettext_noop ("List attestations for EGO"), - &list_attestations), + "credentials", + gettext_noop ("List credentials for EGO"), + &list_credentials), GNUNET_GETOPT_option_string ('I', - "Attestation ID", - "ATTESTATION_ID", + "Credential ID", + "CREDENTIAL_ID", gettext_noop ( - "Attestation to use for attribute"), - &attestation_id), + "Credential to use for attribute"), + &credential_id), GNUNET_GETOPT_option_string ('N', - "attestation-name", + "credential-name", "NAME", - gettext_noop ("Attestation name"), - &attestation_name), + gettext_noop ("Credential name"), + &credential_name), GNUNET_GETOPT_option_string ('i', "issue", "A1,A2,...", @@ -891,6 +890,11 @@ main (int argc, char *const argv[]) "TYPE", gettext_noop ("Type of attribute"), &type_str), + GNUNET_GETOPT_option_string ('u', + "credential-type", + "TYPE", + gettext_noop ("Type of credential"), + &credential_type), GNUNET_GETOPT_option_flag ('T', "tickets", gettext_noop ("List tickets of ego"), diff --git a/src/reclaim/gnunet-service-reclaim.c b/src/reclaim/gnunet-service-reclaim.c index 84afd482e..30a84b3e8 100644 --- a/src/reclaim/gnunet-service-reclaim.c +++ b/src/reclaim/gnunet-service-reclaim.c @@ -170,17 +170,17 @@ struct IdpClient /** * Head of the DLL of - * Attribute iteration operations in + * Credential iteration operations in * progress initiated by this client */ - struct Iterator *attest_iter_head; + struct Iterator *cred_iter_head; /** * Tail of the DLL of - * Attribute iteration operations + * Credential iteration operations * in progress initiated by this client */ - struct Iterator *attest_iter_tail; + struct Iterator *cred_iter_tail; /** * Head of DLL of ticket iteration ops @@ -285,9 +285,9 @@ struct AttributeDeleteHandle struct GNUNET_RECLAIM_Attribute *claim; /** - * The attestation to delete + * The credential to delete */ - struct GNUNET_RECLAIM_Attestation *attest; + struct GNUNET_RECLAIM_Credential *credential; /** * Tickets to update @@ -352,9 +352,9 @@ struct AttributeStoreHandle struct GNUNET_RECLAIM_Attribute *claim; /** - * The attestation to store + * The credential to store */ - struct GNUNET_RECLAIM_Attestation *attest; + struct GNUNET_RECLAIM_Credential *credential; /** * The attribute expiration interval @@ -488,8 +488,8 @@ cleanup_adh (struct AttributeDeleteHandle *adh) GNUNET_free (adh->label); if (NULL != adh->claim) GNUNET_free (adh->claim); - if (NULL != adh->attest) - GNUNET_free (adh->attest); + if (NULL != adh->credential) + GNUNET_free (adh->credential); while (NULL != (le = adh->tickets_to_update_head)) { GNUNET_CONTAINER_DLL_remove (adh->tickets_to_update_head, @@ -517,8 +517,8 @@ cleanup_as_handle (struct AttributeStoreHandle *ash) GNUNET_NAMESTORE_cancel (ash->ns_qe); if (NULL != ash->claim) GNUNET_free (ash->claim); - if (NULL != ash->attest) - GNUNET_free (ash->attest); + if (NULL != ash->credential) + GNUNET_free (ash->credential); GNUNET_free (ash); } @@ -569,9 +569,9 @@ cleanup_client (struct IdpClient *idp) GNUNET_CONTAINER_DLL_remove (idp->attr_iter_head, idp->attr_iter_tail, ai); GNUNET_free (ai); } - while (NULL != (ai = idp->attest_iter_head)) + while (NULL != (ai = idp->cred_iter_head)) { - GNUNET_CONTAINER_DLL_remove (idp->attest_iter_head, idp->attest_iter_tail, + GNUNET_CONTAINER_DLL_remove (idp->cred_iter_head, idp->cred_iter_tail, ai); GNUNET_free (ai); } @@ -847,7 +847,7 @@ static void consume_result_cb (void *cls, const struct GNUNET_CRYPTO_EcdsaPublicKey *identity, const struct GNUNET_RECLAIM_AttributeList *attrs, - const struct GNUNET_RECLAIM_AttestationList *attests, + const struct GNUNET_RECLAIM_PresentationList *presentations, int32_t success, const char *emsg) { @@ -856,28 +856,28 @@ consume_result_cb (void *cls, struct GNUNET_MQ_Envelope *env; char *data_tmp; size_t attrs_len = 0; - size_t attests_len = 0; + size_t pres_len = 0; if (GNUNET_OK != success) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error consuming ticket: %s\n", emsg); } attrs_len = GNUNET_RECLAIM_attribute_list_serialize_get_size (attrs); - attests_len = GNUNET_RECLAIM_attestation_list_serialize_get_size (attests); + pres_len = GNUNET_RECLAIM_presentation_list_serialize_get_size (presentations); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending CONSUME_TICKET_RESULT message\n"); env = GNUNET_MQ_msg_extra (crm, - attrs_len + attests_len, + attrs_len + pres_len, GNUNET_MESSAGE_TYPE_RECLAIM_CONSUME_TICKET_RESULT); crm->id = htonl (cop->r_id); crm->attrs_len = htons (attrs_len); - crm->attestations_len = htons (attests_len); + crm->pres_len = htons (pres_len); crm->identity = *identity; crm->result = htonl (success); data_tmp = (char *) &crm[1]; GNUNET_RECLAIM_attribute_list_serialize (attrs, data_tmp); data_tmp += attrs_len; - GNUNET_RECLAIM_attestation_list_serialize (attests, data_tmp); + GNUNET_RECLAIM_presentation_list_serialize (presentations, data_tmp); GNUNET_MQ_send (cop->client->mq, env); GNUNET_CONTAINER_DLL_remove (cop->client->consume_op_head, cop->client->consume_op_tail, @@ -1075,14 +1075,14 @@ handle_attribute_store_message (void *cls, /** - * Attestation store result handler + * Credential store result handler * * @param cls our attribute store handle * @param success GNUNET_OK if successful * @param emsg error message (NULL if success=GNUNET_OK) */ static void -attest_store_cont (void *cls, int32_t success, const char *emsg) +cred_store_cont (void *cls, int32_t success, const char *emsg) { struct AttributeStoreHandle *ash = cls; struct GNUNET_MQ_Envelope *env; @@ -1096,7 +1096,7 @@ attest_store_cont (void *cls, int32_t success, const char *emsg) if (GNUNET_SYSERR == success) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to store attestation %s\n", + "Failed to store credential: %s\n", emsg); cleanup_as_handle (ash); GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); @@ -1113,16 +1113,16 @@ attest_store_cont (void *cls, int32_t success, const char *emsg) /** - * Error looking up potential attestation. Abort. + * Error looking up potential credential. Abort. * * @param cls our attribute store handle */ static void -attest_error (void *cls) +cred_error (void *cls) { struct AttributeStoreHandle *ash = cls; GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to check for existing Attestation\n"); + "Failed to check for existing credential.\n"); cleanup_as_handle (ash); GNUNET_SCHEDULER_add_now (&do_shutdown, NULL); return; @@ -1130,7 +1130,7 @@ attest_error (void *cls) /** -* Check for existing record before storing attestation +* Check for existing record before storing credential * * @param cls our attribute store handle * @param zone zone we are iterating @@ -1139,33 +1139,34 @@ attest_error (void *cls) * @param rd records */ static void -attest_add_cb (void *cls, - const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) +cred_add_cb (void *cls, + const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) { struct AttributeStoreHandle *ash = cls; + struct GNUNET_GNSRECORD_Data rd_new[1]; char *buf; size_t buf_size; - buf_size = GNUNET_RECLAIM_attestation_serialize_get_size (ash->attest); + + buf_size = GNUNET_RECLAIM_credential_serialize_get_size (ash->credential); buf = GNUNET_malloc (buf_size); - GNUNET_RECLAIM_attestation_serialize (ash->attest, buf); + GNUNET_RECLAIM_presentation_serialize (ash->credential, buf); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Storing new Attestation\n"); - struct GNUNET_GNSRECORD_Data rd_new[1]; + "Storing new credential under `%s'.\n", + label); rd_new[0].data_size = buf_size; rd_new[0].data = buf; - rd_new[0].record_type = GNUNET_GNSRECORD_TYPE_RECLAIM_ATTESTATION; + rd_new[0].record_type = GNUNET_GNSRECORD_TYPE_RECLAIM_CREDENTIAL; rd_new[0].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; rd_new[0].expiration_time = ash->exp.rel_value_us; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Encrypting with label %s\n", label); ash->ns_qe = GNUNET_NAMESTORE_records_store (nsh, &ash->identity, label, 1, rd_new, - &attest_store_cont, + &cred_store_cont, ash); GNUNET_free (buf); return; @@ -1173,44 +1174,43 @@ attest_add_cb (void *cls, /** - * Add a new attestation + * Add a new credential * * @param cls the AttributeStoreHandle */ static void -attest_store_task (void *cls) +cred_store_task (void *cls) { struct AttributeStoreHandle *ash = cls; char *label; // Give the ash a new id if unset - if (GNUNET_YES == GNUNET_RECLAIM_id_is_zero (&ash->attest->id)) - GNUNET_RECLAIM_id_generate (&ash->attest->id); - label = GNUNET_STRINGS_data_to_string_alloc (&ash->attest->id, - sizeof (ash->attest->id)); + if (GNUNET_YES == GNUNET_RECLAIM_id_is_zero (&ash->credential->id)) + GNUNET_RECLAIM_id_generate (&ash->credential->id); + label = GNUNET_STRINGS_data_to_string_alloc (&ash->credential->id, + sizeof (ash->credential->id)); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Looking up existing data under label %s\n", label); -// Test for the content of the existing ID + "Looking up existing data under label `%s'\n", label); ash->ns_qe = GNUNET_NAMESTORE_records_lookup (nsh, &ash->identity, label, - &attest_error, + &cred_error, ash, - &attest_add_cb, + &cred_add_cb, ash); GNUNET_free (label); } /** - * Check an attestation store message + * Check an credential store message * * @param cls unused * @param sam the message to check */ static int -check_attestation_store_message (void *cls, - const struct AttributeStoreMessage *sam) +check_credential_store_message (void *cls, + const struct AttributeStoreMessage *sam) { uint16_t size; @@ -1225,26 +1225,26 @@ check_attestation_store_message (void *cls, /** -* Handle an attestation store message +* Handle a credential store message * * @param cls our client * @param sam the message to handle */ static void -handle_attestation_store_message (void *cls, +handle_credential_store_message (void *cls, const struct AttributeStoreMessage *sam) { struct AttributeStoreHandle *ash; struct IdpClient *idp = cls; size_t data_len; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received ATTESTATION_STORE message\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received CREDENTIAL_STORE message\n"); data_len = ntohs (sam->attr_len); ash = GNUNET_new (struct AttributeStoreHandle); - ash->attest = GNUNET_RECLAIM_attestation_deserialize ((char *) &sam[1], - data_len); + ash->credential = GNUNET_RECLAIM_credential_deserialize ((char *) &sam[1], + data_len); ash->r_id = ntohl (sam->id); ash->identity = sam->identity; @@ -1254,7 +1254,7 @@ handle_attestation_store_message (void *cls, GNUNET_SERVICE_client_continue (idp->client); ash->client = idp; GNUNET_CONTAINER_DLL_insert (idp->store_op_head, idp->store_op_tail, ash); - GNUNET_SCHEDULER_add_now (&attest_store_task, ash); + GNUNET_SCHEDULER_add_now (&cred_store_task, ash); } @@ -1310,12 +1310,12 @@ ticket_iter (void *cls, if (GNUNET_YES != GNUNET_RECLAIM_id_is_equal (rd[i].data, &adh->claim->id)) continue; - if (adh->attest != NULL) + if (adh->credential != NULL) if (GNUNET_YES != GNUNET_RECLAIM_id_is_equal (rd[i].data, - &adh->attest->id)) + &adh->credential->id)) continue; GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Attribute or Attestation to delete found (%s)\n", + "Attribute to delete found (%s)\n", adh->label); has_changed = GNUNET_YES; break; @@ -1410,10 +1410,10 @@ update_tickets (void *cls) && (GNUNET_YES == GNUNET_RECLAIM_id_is_equal (rd[i].data, &adh->claim->id))) continue; - if (adh->attest != NULL) + if (adh->credential != NULL) if ((GNUNET_GNSRECORD_TYPE_RECLAIM_ATTRIBUTE_REF == rd[i].record_type) && (GNUNET_YES == GNUNET_RECLAIM_id_is_equal (rd[i].data, - &adh->attest->id))) + &adh->credential->id))) continue; rd_new[j] = rd[i]; j++; @@ -1557,7 +1557,7 @@ handle_attribute_delete_message (void *cls, GNUNET_RECLAIM_attribute_deserialize ((char *) &dam[1], data_len, &adh->claim); - adh->attest = NULL; + adh->credential = NULL; adh->r_id = ntohl (dam->id); adh->identity = dam->identity; @@ -1578,14 +1578,14 @@ handle_attribute_delete_message (void *cls, /** - * Attestation deleted callback + * Credential deleted callback * * @param cls our handle * @param success success status * @param emsg error message (NULL if success=GNUNET_OK) */ static void -attest_delete_cont (void *cls, int32_t success, const char *emsg) +cred_delete_cont (void *cls, int32_t success, const char *emsg) { struct AttributeDeleteHandle *adh = cls; @@ -1593,7 +1593,7 @@ attest_delete_cont (void *cls, int32_t success, const char *emsg) if (GNUNET_SYSERR == success) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Error deleting attestation %s\n", + "Error deleting credential `%s'\n", adh->label); send_delete_response (adh, GNUNET_SYSERR); cleanup_adh (adh); @@ -1605,13 +1605,13 @@ attest_delete_cont (void *cls, int32_t success, const char *emsg) /** - * Check attestation delete message format + * Check credential delete message format * * @cls unused * @dam message to check */ static int -check_attestation_delete_message (void *cls, +check_cred_delete_message (void *cls, const struct AttributeDeleteMessage *dam) { uint16_t size; @@ -1627,33 +1627,33 @@ check_attestation_delete_message (void *cls, /** - * Handle attestation deletion + * Handle credential deletion * * @param cls our client * @param dam deletion message */ static void -handle_attestation_delete_message (void *cls, +handle_credential_delete_message (void *cls, const struct AttributeDeleteMessage *dam) { struct AttributeDeleteHandle *adh; struct IdpClient *idp = cls; size_t data_len; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received ATTESTATION_DELETE message\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received CREDENTIAL_DELETE message\n"); data_len = ntohs (dam->attr_len); adh = GNUNET_new (struct AttributeDeleteHandle); - adh->attest = GNUNET_RECLAIM_attestation_deserialize ((char *) &dam[1], - data_len); + adh->credential = GNUNET_RECLAIM_credential_deserialize ((char *) &dam[1], + data_len); adh->claim = NULL; adh->r_id = ntohl (dam->id); adh->identity = dam->identity; adh->label - = GNUNET_STRINGS_data_to_string_alloc (&adh->attest->id, - sizeof(adh->attest->id)); + = GNUNET_STRINGS_data_to_string_alloc (&adh->credential->id, + sizeof(adh->credential->id)); GNUNET_SERVICE_client_continue (idp->client); adh->client = idp; GNUNET_CONTAINER_DLL_insert (idp->delete_op_head, idp->delete_op_tail, adh); @@ -1662,7 +1662,7 @@ handle_attestation_delete_message (void *cls, adh->label, 0, NULL, - &attest_delete_cont, + &cred_delete_cont, adh); } @@ -1712,7 +1712,7 @@ attr_iter_error (void *cls) /** - * Got record. Return if it is an attribute or attestation. + * Got record. Return if it is an attribute. * * @param cls our attribute iterator * @param zone zone we are iterating @@ -1852,51 +1852,51 @@ handle_iteration_next (void *cls, /************************************************* -* Attestation iteration +* Credential iteration *************************************************/ /** - * Done iterating over attestations + * Done iterating over credentials * * @param cls our iterator handle */ static void -attest_iter_finished (void *cls) +cred_iter_finished (void *cls) { struct Iterator *ai = cls; struct GNUNET_MQ_Envelope *env; - struct AttestationResultMessage *arm; + struct CredentialResultMessage *arm; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending ATTESTATION_RESULT message\n"); - env = GNUNET_MQ_msg (arm, GNUNET_MESSAGE_TYPE_RECLAIM_ATTESTATION_RESULT); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending CREDENTIAL_RESULT message\n"); + env = GNUNET_MQ_msg (arm, GNUNET_MESSAGE_TYPE_RECLAIM_CREDENTIAL_RESULT); arm->id = htonl (ai->request_id); - arm->attestation_len = htons (0); + arm->credential_len = htons (0); GNUNET_MQ_send (ai->client->mq, env); - GNUNET_CONTAINER_DLL_remove (ai->client->attest_iter_head, - ai->client->attest_iter_tail, + GNUNET_CONTAINER_DLL_remove (ai->client->cred_iter_head, + ai->client->cred_iter_tail, ai); GNUNET_free (ai); } /** - * Error iterating over attestations. Abort. + * Error iterating over credentials. Abort. * * @param cls our attribute iteration handle */ static void -attest_iter_error (void *cls) +cred_iter_error (void *cls) { struct Iterator *ai = cls; - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to iterate over attestations\n"); - attest_iter_finished (ai); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to iterate over credentials\n"); + cred_iter_finished (ai); } /** - * Got record. Return attestation. + * Got record. Return credential. * * @param cls our attribute iterator * @param zone zone we are iterating @@ -1905,32 +1905,32 @@ attest_iter_error (void *cls) * @param rd records */ static void -attest_iter_cb (void *cls, - const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) +cred_iter_cb (void *cls, + const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) { struct Iterator *ai = cls; struct GNUNET_MQ_Envelope *env; - struct AttestationResultMessage *arm; + struct CredentialResultMessage *arm; char *data_tmp; if ((rd_count != 1) || - (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTESTATION != rd->record_type)) + (GNUNET_GNSRECORD_TYPE_RECLAIM_CREDENTIAL != rd->record_type)) { GNUNET_NAMESTORE_zone_iterator_next (ai->ns_it, 1); return; } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found attestation under: %s\n", + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found credential under: %s\n", label); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending ATTESTATION_RESULT message\n"); + "Sending CREDENTIAL_RESULT message\n"); env = GNUNET_MQ_msg_extra (arm, rd->data_size, - GNUNET_MESSAGE_TYPE_RECLAIM_ATTESTATION_RESULT); + GNUNET_MESSAGE_TYPE_RECLAIM_CREDENTIAL_RESULT); arm->id = htonl (ai->request_id); - arm->attestation_len = htons (rd->data_size); + arm->credential_len = htons (rd->data_size); GNUNET_CRYPTO_ecdsa_key_get_public (zone, &arm->identity); data_tmp = (char *) &arm[1]; GNUNET_memcpy (data_tmp, rd->data, rd->data_size); @@ -1946,29 +1946,29 @@ attest_iter_cb (void *cls, * @param ais_msg the iteration message to start */ static void -handle_attestation_iteration_start (void *cls, - const struct - AttestationIterationStartMessage *ais_msg) +handle_credential_iteration_start (void *cls, + const struct + CredentialIterationStartMessage *ais_msg) { struct IdpClient *idp = cls; struct Iterator *ai; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received ATTESTATION_ITERATION_START message\n"); + "Received CREDENTIAL_ITERATION_START message\n"); ai = GNUNET_new (struct Iterator); ai->request_id = ntohl (ais_msg->id); ai->client = idp; ai->identity = ais_msg->identity; - GNUNET_CONTAINER_DLL_insert (idp->attest_iter_head, idp->attest_iter_tail, + GNUNET_CONTAINER_DLL_insert (idp->cred_iter_head, idp->cred_iter_tail, ai); ai->ns_it = GNUNET_NAMESTORE_zone_iteration_start (nsh, &ai->identity, - &attest_iter_error, + &cred_iter_error, ai, - &attest_iter_cb, + &cred_iter_cb, ai, - &attest_iter_finished, + &cred_iter_finished, ai); GNUNET_SERVICE_client_continue (idp->client); } @@ -1981,9 +1981,9 @@ handle_attestation_iteration_start (void *cls, * @param ais_msg the stop message */ static void -handle_attestation_iteration_stop (void *cls, - const struct - AttestationIterationStopMessage *ais_msg) +handle_credential_iteration_stop (void *cls, + const struct + CredentialIterationStopMessage *ais_msg) { struct IdpClient *idp = cls; struct Iterator *ai; @@ -1991,9 +1991,9 @@ handle_attestation_iteration_stop (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received `%s' message\n", - "ATTESTATION_ITERATION_STOP"); + "CREDENTIAL_ITERATION_STOP"); rid = ntohl (ais_msg->id); - for (ai = idp->attest_iter_head; NULL != ai; ai = ai->next) + for (ai = idp->cred_iter_head; NULL != ai; ai = ai->next) if (ai->request_id == rid) break; if (NULL == ai) @@ -2002,7 +2002,7 @@ handle_attestation_iteration_stop (void *cls, GNUNET_SERVICE_client_drop (idp->client); return; } - GNUNET_CONTAINER_DLL_remove (idp->attest_iter_head, idp->attest_iter_tail, + GNUNET_CONTAINER_DLL_remove (idp->cred_iter_head, idp->cred_iter_tail, ai); GNUNET_free (ai); GNUNET_SERVICE_client_continue (idp->client); @@ -2010,24 +2010,24 @@ handle_attestation_iteration_stop (void *cls, /** - * Client requests next attestation from iterator + * Client requests next credential from iterator * * @param cls the client * @param ais_msg the message */ static void -handle_attestation_iteration_next (void *cls, - const struct - AttestationIterationNextMessage *ais_msg) +handle_credential_iteration_next (void *cls, + const struct + CredentialIterationNextMessage *ais_msg) { struct IdpClient *idp = cls; struct Iterator *ai; uint32_t rid; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Received ATTESTATION_ITERATION_NEXT message\n"); + "Received CREDENTIAL_ITERATION_NEXT message\n"); rid = ntohl (ais_msg->id); - for (ai = idp->attest_iter_head; NULL != ai; ai = ai->next) + for (ai = idp->cred_iter_head; NULL != ai; ai = ai->next) if (ai->request_id == rid) break; if (NULL == ai) @@ -2269,16 +2269,16 @@ GNUNET_SERVICE_MAIN ( GNUNET_MESSAGE_TYPE_RECLAIM_ATTRIBUTE_STORE, struct AttributeStoreMessage, NULL), - GNUNET_MQ_hd_var_size (attestation_store_message, - GNUNET_MESSAGE_TYPE_RECLAIM_ATTESTATION_STORE, + GNUNET_MQ_hd_var_size (credential_store_message, + GNUNET_MESSAGE_TYPE_RECLAIM_CREDENTIAL_STORE, struct AttributeStoreMessage, NULL), GNUNET_MQ_hd_var_size (attribute_delete_message, GNUNET_MESSAGE_TYPE_RECLAIM_ATTRIBUTE_DELETE, struct AttributeDeleteMessage, NULL), - GNUNET_MQ_hd_var_size (attestation_delete_message, - GNUNET_MESSAGE_TYPE_RECLAIM_ATTESTATION_DELETE, + GNUNET_MQ_hd_var_size (credential_delete_message, + GNUNET_MESSAGE_TYPE_RECLAIM_CREDENTIAL_DELETE, struct AttributeDeleteMessage, NULL), GNUNET_MQ_hd_fixed_size (iteration_start, @@ -2293,17 +2293,17 @@ GNUNET_SERVICE_MAIN ( GNUNET_MESSAGE_TYPE_RECLAIM_ATTRIBUTE_ITERATION_STOP, struct AttributeIterationStopMessage, NULL), - GNUNET_MQ_hd_fixed_size (attestation_iteration_start, - GNUNET_MESSAGE_TYPE_RECLAIM_ATTESTATION_ITERATION_START, - struct AttestationIterationStartMessage, + GNUNET_MQ_hd_fixed_size (credential_iteration_start, + GNUNET_MESSAGE_TYPE_RECLAIM_CREDENTIAL_ITERATION_START, + struct CredentialIterationStartMessage, NULL), - GNUNET_MQ_hd_fixed_size (attestation_iteration_next, - GNUNET_MESSAGE_TYPE_RECLAIM_ATTESTATION_ITERATION_NEXT, - struct AttestationIterationNextMessage, + GNUNET_MQ_hd_fixed_size (credential_iteration_next, + GNUNET_MESSAGE_TYPE_RECLAIM_CREDENTIAL_ITERATION_NEXT, + struct CredentialIterationNextMessage, NULL), - GNUNET_MQ_hd_fixed_size (attestation_iteration_stop, - GNUNET_MESSAGE_TYPE_RECLAIM_ATTESTATION_ITERATION_STOP, - struct AttestationIterationStopMessage, + GNUNET_MQ_hd_fixed_size (credential_iteration_stop, + GNUNET_MESSAGE_TYPE_RECLAIM_CREDENTIAL_ITERATION_STOP, + struct CredentialIterationStopMessage, NULL), GNUNET_MQ_hd_var_size (issue_ticket_message, diff --git a/src/reclaim/gnunet-service-reclaim_tickets.c b/src/reclaim/gnunet-service-reclaim_tickets.c index af01d8ec7..18b173aaa 100644 --- a/src/reclaim/gnunet-service-reclaim_tickets.c +++ b/src/reclaim/gnunet-service-reclaim_tickets.c @@ -114,9 +114,9 @@ struct RECLAIM_TICKETS_ConsumeHandle struct GNUNET_RECLAIM_AttributeList *attrs; /** - * Attestations + * Credentials */ - struct GNUNET_RECLAIM_AttestationList *attests; + struct GNUNET_RECLAIM_CredentialList *credentials; /** * Lookup time @@ -715,20 +715,20 @@ rvk_move_attr_cb (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute %s\n", new_label); GNUNET_free (claim); } - else if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTESTATION == rd[i].record_type) + else if (GNUNET_GNSRECORD_TYPE_RECLAIM_CREDENTIAL == rd[i].record_type) { - struct GNUNET_RECLAIM_Attestation *attest; - attest = GNUNET_RECLAIM_attestation_deserialize (rd[i].data, - rd[i].data_size); + struct GNUNET_RECLAIM_Credential *credential; + credential = GNUNET_RECLAIM_credential_deserialize (rd[i].data, + rd[i].data_size); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Attestation to update: Name=%s\n", - attest->name); - attest->id = rvk->move_attr->new_id; + "Credential to update: Name=%s\n", + credential->name); + credential->id = rvk->move_attr->new_id; new_rd[i].data_size = - GNUNET_RECLAIM_attestation_serialize_get_size (attest); + GNUNET_RECLAIM_credential_serialize_get_size (credential); attr_data = GNUNET_malloc (rd[i].data_size); - new_rd[i].data_size = GNUNET_RECLAIM_attestation_serialize (attest, - attr_data); + new_rd[i].data_size = GNUNET_RECLAIM_credential_serialize (credential, + attr_data); new_rd[i].data = attr_data; new_rd[i].record_type = rd[i].record_type; new_rd[i].flags = rd[i].flags; @@ -736,9 +736,9 @@ rvk_move_attr_cb (void *cls, new_label = GNUNET_STRINGS_data_to_string_alloc (&rvk->move_attr->new_id, sizeof (rvk->move_attr->new_id)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attestation %s\n", + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding credential %s\n", new_label); - GNUNET_free (attest); + GNUNET_free (credential); } } rvk->ns_qe = GNUNET_NAMESTORE_records_store (nsh, @@ -981,8 +981,8 @@ cleanup_cth (struct RECLAIM_TICKETS_ConsumeHandle *cth) if (NULL != cth->attrs) GNUNET_RECLAIM_attribute_list_destroy (cth->attrs); - if (NULL != cth->attests) - GNUNET_RECLAIM_attestation_list_destroy (cth->attests); + if (NULL != cth->credentials) + GNUNET_RECLAIM_credential_list_destroy (cth->credentials); GNUNET_free (cth); } @@ -1027,40 +1027,20 @@ process_parallel_lookup_result (void *cls, // REMARK: It is possible now to find rd_count > 1 for (int i = 0; i < rd_count; i++) { - if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTRIBUTE == rd[i].record_type) - { - attr_le = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry); - GNUNET_RECLAIM_attribute_deserialize (rd[i].data, rd[i].data_size, - &attr_le->attribute); - GNUNET_CONTAINER_DLL_insert (cth->attrs->list_head, - cth->attrs->list_tail, - attr_le); - } - else if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTESTATION == rd[i].record_type) - { - struct GNUNET_RECLAIM_AttestationListEntry *ale; - ale = GNUNET_new (struct GNUNET_RECLAIM_AttestationListEntry); - ale->attestation = - GNUNET_RECLAIM_attestation_deserialize (rd[i].data, - rd[i].data_size); - GNUNET_CONTAINER_DLL_insert (cth->attests->list_head, - cth->attests->list_tail, - ale); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Parallel Lookup of Reference without Attestation"); + if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTRIBUTE != rd[i].record_type) continue; - } - - + attr_le = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry); + GNUNET_RECLAIM_attribute_deserialize (rd[i].data, rd[i].data_size, + &attr_le->attribute); + GNUNET_CONTAINER_DLL_insert (cth->attrs->list_head, + cth->attrs->list_tail, + attr_le); } if (NULL != cth->parallel_lookups_head) return; // Wait for more /* Else we are done */ cth->cb (cth->cb_cls, &cth->ticket.identity, - cth->attrs, cth->attests, GNUNET_OK, NULL); + cth->attrs, cth->credentials, GNUNET_OK, NULL); cleanup_cth (cth); } @@ -1126,26 +1106,45 @@ lookup_authz_cb (void *cls, for (int i = 0; i < rd_count; i++) { - if ((GNUNET_GNSRECORD_TYPE_RECLAIM_ATTRIBUTE_REF != rd[i].record_type) && - (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTESTATION_REF != rd[i].record_type)) - continue; - lbl = GNUNET_STRINGS_data_to_string_alloc (rd[i].data, rd[i].data_size); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ticket reference found %s\n", lbl); - parallel_lookup = GNUNET_new (struct ParallelLookup); - parallel_lookup->handle = cth; - parallel_lookup->label = lbl; - parallel_lookup->lookup_start_time = GNUNET_TIME_absolute_get (); - parallel_lookup->lookup_request = - GNUNET_GNS_lookup (gns, - lbl, - &cth->ticket.identity, - GNUNET_GNSRECORD_TYPE_ANY, - GNUNET_GNS_LO_DEFAULT, - &process_parallel_lookup_result, - parallel_lookup); - GNUNET_CONTAINER_DLL_insert (cth->parallel_lookups_head, - cth->parallel_lookups_tail, - parallel_lookup); + /** + * Check if record is a credential presentation or an attribute + * reference. + */ + switch (rd[i].record_type) + { + case GNUNET_GNSRECORD_TYPE_RECLAIM_PRESENTATION: + struct GNUNET_RECLAIM_CredentialListEntry *ale; + ale = GNUNET_new (struct GNUNET_RECLAIM_CredentialListEntry); + ale->credential = + GNUNET_RECLAIM_credential_deserialize (rd[i].data, + rd[i].data_size); + GNUNET_CONTAINER_DLL_insert (cth->credentials->list_head, + cth->credentials->list_tail, + ale); + break; + case GNUNET_GNSRECORD_TYPE_RECLAIM_ATTRIBUTE_REF: + lbl = GNUNET_STRINGS_data_to_string_alloc (rd[i].data, rd[i].data_size); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ticket reference found %s\n", lbl); + parallel_lookup = GNUNET_new (struct ParallelLookup); + parallel_lookup->handle = cth; + parallel_lookup->label = lbl; + parallel_lookup->lookup_start_time = GNUNET_TIME_absolute_get (); + parallel_lookup->lookup_request = + GNUNET_GNS_lookup (gns, + lbl, + &cth->ticket.identity, + GNUNET_GNSRECORD_TYPE_ANY, + GNUNET_GNS_LO_DEFAULT, + &process_parallel_lookup_result, + parallel_lookup); + GNUNET_CONTAINER_DLL_insert (cth->parallel_lookups_head, + cth->parallel_lookups_tail, + parallel_lookup); + break; + default: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Ignoring unknown record type %d", rd[i].record_type); + } } /** * We started lookups. Add a timeout task. @@ -1163,7 +1162,7 @@ lookup_authz_cb (void *cls, * No references found, return empty attribute list */ cth->cb (cth->cb_cls, &cth->ticket.identity, - cth->attrs, cth->attests, GNUNET_OK, NULL); + cth->attrs, cth->credentials, GNUNET_OK, NULL); cleanup_cth (cth); } @@ -1193,7 +1192,7 @@ RECLAIM_TICKETS_consume (const struct GNUNET_CRYPTO_EcdsaPrivateKey *id, cth->identity = *id; GNUNET_CRYPTO_ecdsa_key_get_public (&cth->identity, &cth->identity_pub); cth->attrs = GNUNET_new (struct GNUNET_RECLAIM_AttributeList); - cth->attests = GNUNET_new (struct GNUNET_RECLAIM_AttestationList); + cth->credentials = GNUNET_new (struct GNUNET_RECLAIM_CredentialList); cth->ticket = *ticket; cth->cb = cb; cth->cb_cls = cb_cls; @@ -1293,7 +1292,7 @@ issue_ticket (struct TicketIssueHandle *ih) for (le = ih->attrs->list_head; NULL != le; le = le->next) attrs_count++; - // Worst case we have one attestation per attribute + // Worst case we have one credential per attribute attrs_record = GNUNET_malloc (2 * attrs_count * sizeof(struct GNUNET_GNSRECORD_Data)); i = 0; @@ -1308,26 +1307,31 @@ issue_ticket (struct TicketIssueHandle *ih) attrs_record[i].record_type = GNUNET_GNSRECORD_TYPE_RECLAIM_ATTRIBUTE_REF; attrs_record[i].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; i++; - if (GNUNET_NO == GNUNET_RECLAIM_id_is_zero (&le->attribute->attestation)) + if (GNUNET_NO == GNUNET_RECLAIM_id_is_zero (&le->attribute->credential)) { + struct GNUNET_RECLAIM_Presentation *pres; int j; for (j = 0; j < i; j++) { if (attrs_record[j].record_type - != GNUNET_GNSRECORD_TYPE_RECLAIM_ATTESTATION_REF) + != GNUNET_GNSRECORD_TYPE_RECLAIM_PRESENTATION) continue; - if (0 == memcmp (attrs_record[j].data, - &le->attribute->attestation, - sizeof (le->attribute->attestation))) + pres = attrs_record[j].data; + if (0 == memcmp (pres->credential_id, + &le->attribute->credential, + sizeof (le->attribute->credential))) break; } if (j < i) - continue; // Skip as we have already added this attestation. - attrs_record[i].data = &le->attribute->attestation; - attrs_record[i].data_size = sizeof(le->attribute->attestation); + continue; // Skip as we have already added this credential presentation. + /** + * FIXME: Create a new presentation from the credential. + */ + attrs_record[i].data = &le->attribute->credential; + attrs_record[i].data_size = sizeof(le->attribute->credential); attrs_record[i].expiration_time = ticket_refresh_interval.rel_value_us; attrs_record[i].record_type = - GNUNET_GNSRECORD_TYPE_RECLAIM_ATTESTATION_REF; + GNUNET_GNSRECORD_TYPE_RECLAIM_PRESENTATION; attrs_record[i].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; i++; } @@ -1401,22 +1405,23 @@ filter_tickets_cb (void *cls, { struct TicketIssueHandle *tih = cls; struct GNUNET_RECLAIM_Ticket *ticket = NULL; + struct GNUNET_RECLAIM_Presentation *pres; // figure out the number of requested attributes struct GNUNET_RECLAIM_AttributeListEntry *le; unsigned int attr_cnt = 0; - unsigned int attest_cnt = 0; + unsigned int cred_cnt = 0; for (le = tih->attrs->list_head; NULL != le; le = le->next) { attr_cnt++; - if (GNUNET_NO == GNUNET_RECLAIM_id_is_zero (&le->attribute->attestation)) - attest_cnt++; + if (GNUNET_NO == GNUNET_RECLAIM_id_is_zero (&le->attribute->credential)) + pres_cnt++; } // ticket search unsigned int found_attrs_cnt = 0; - unsigned int found_attests_cnt = 0; + unsigned int found_pres_cnt = 0; for (int i = 0; i < rd_count; i++) { @@ -1436,20 +1441,25 @@ filter_tickets_cb (void *cls, } // cmp requested attributes with ticket attributes - if ((GNUNET_GNSRECORD_TYPE_RECLAIM_ATTRIBUTE_REF != rd[i].record_type) && - (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTESTATION_REF != rd[i].record_type)) - continue; - for (le = tih->attrs->list_head; NULL != le; le = le->next) + if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTRIBUTE_REF == rd[i].record_type) { - if (GNUNET_YES == GNUNET_RECLAIM_id_is_equal (rd[i].data, - &le->attribute->id)) - found_attrs_cnt++; + for (le = tih->attrs->list_head; NULL != le; le = le->next) + { + if (GNUNET_YES == GNUNET_RECLAIM_id_is_equal (rd[i].data, + &le->attribute->id)) + found_attrs_cnt++; + } } - for (le = tih->attrs->list_head; NULL != le; le = le->next) + if (GNUNET_GNSRECORD_TYPE_RECLAIM_PRESENTATION == rd[i].record_type) { - if (GNUNET_YES == GNUNET_RECLAIM_id_is_equal (rd[i].data, - &le->attribute->attestation)) - found_attests_cnt++; + for (le = tih->attrs->list_head; NULL != le; le = le->next) + { + pres = rd[i].data; + if (GNUNET_YES == GNUNET_RECLAIM_id_is_equal (pres->credential_id, + &le->attribute->credential)) + found_pres_cnt++; + // FIXME should we store credentials here for later use?? + } } } @@ -1458,7 +1468,7 @@ filter_tickets_cb (void *cls, * we are done. */ if ((attr_cnt == found_attrs_cnt) && - (attest_cnt == found_attests_cnt) && + (pres_cnt == found_pres_cnt) && (NULL != ticket)) { GNUNET_NAMESTORE_zone_iteration_stop (tih->ns_it); diff --git a/src/reclaim/gnunet-service-reclaim_tickets.h b/src/reclaim/gnunet-service-reclaim_tickets.h index 1c7214d42..404b9c837 100644 --- a/src/reclaim/gnunet-service-reclaim_tickets.h +++ b/src/reclaim/gnunet-service-reclaim_tickets.h @@ -137,7 +137,7 @@ typedef void (*RECLAIM_TICKETS_ConsumeCallback) ( void *cls, const struct GNUNET_CRYPTO_EcdsaPublicKey *identity, const struct GNUNET_RECLAIM_AttributeList *attributes, - const struct GNUNET_RECLAIM_AttestationList *attestations, + const struct GNUNET_RECLAIM_CredentialList *credentials, int32_t success, const char *emsg); diff --git a/src/reclaim/json_reclaim.c b/src/reclaim/json_reclaim.c index c470ea567..c8b944326 100644 --- a/src/reclaim/json_reclaim.c +++ b/src/reclaim/json_reclaim.c @@ -46,7 +46,7 @@ parse_attr (void *cls, json_t *root, struct GNUNET_JSON_Specification *spec) const char *val_str = NULL; const char *type_str = NULL; const char *id_str = NULL; - const char *attest_str = NULL; + const char *cred_str = NULL; const char *flag_str = NULL; char *data; int unpack_state; @@ -68,8 +68,8 @@ parse_attr (void *cls, json_t *root, struct GNUNET_JSON_Specification *spec) &name_str, "id", &id_str, - "attestation", - &attest_str, + "credential", + &cred_str, "type", &type_str, "value", @@ -95,12 +95,12 @@ parse_attr (void *cls, json_t *root, struct GNUNET_JSON_Specification *spec) } attr = GNUNET_RECLAIM_attribute_new (name_str, NULL, type, data, data_size); - if ((NULL != attest_str) && (0 != strlen (attest_str))) + if ((NULL != cred_str) && (0 != strlen (cred_str))) { - GNUNET_STRINGS_string_to_data (attest_str, - strlen (attest_str), - &attr->attestation, - sizeof(attr->attestation)); + GNUNET_STRINGS_string_to_data (cred_str, + strlen (cred_str), + &attr->credential, + sizeof(attr->credential)); } if ((NULL == id_str) || (0 == strlen (id_str))) memset (&attr->id, 0, sizeof (attr->id)); @@ -142,7 +142,7 @@ clean_attr (void *cls, struct GNUNET_JSON_Specification *spec) * @return JSON Specification */ struct GNUNET_JSON_Specification -GNUNET_RECLAIM_JSON_spec_claim (struct GNUNET_RECLAIM_Attribute **attr) +GNUNET_RECLAIM_JSON_spec_attribute (struct GNUNET_RECLAIM_Attribute **attr) { struct GNUNET_JSON_Specification ret = { .parser = &parse_attr, .cleaner = &clean_attr, @@ -279,7 +279,7 @@ GNUNET_RECLAIM_JSON_spec_ticket (struct GNUNET_RECLAIM_Ticket **ticket) /** - * Parse given JSON object to an attestation claim + * Parse given JSON object to a credential claim * * @param cls closure, NULL * @param root the json object representing data @@ -287,9 +287,9 @@ GNUNET_RECLAIM_JSON_spec_ticket (struct GNUNET_RECLAIM_Ticket **ticket) * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error */ static int -parse_attest (void *cls, json_t *root, struct GNUNET_JSON_Specification *spec) +parse_credential (void *cls, json_t *root, struct GNUNET_JSON_Specification *spec) { - struct GNUNET_RECLAIM_Attestation *attr; + struct GNUNET_RECLAIM_Credential *cred; const char *name_str = NULL; const char *val_str = NULL; const char *type_str = NULL; @@ -325,17 +325,17 @@ parse_attest (void *cls, json_t *root, struct GNUNET_JSON_Specification *spec) "Error json object has a wrong format!\n"); return GNUNET_SYSERR; } - type = GNUNET_RECLAIM_attestation_typename_to_number (type_str); + type = GNUNET_RECLAIM_credential_typename_to_number (type_str); if (GNUNET_SYSERR == - (GNUNET_RECLAIM_attestation_string_to_value (type, - val_str, - (void **) &data, - &data_size))) + (GNUNET_RECLAIM_credential_string_to_value (type, + val_str, + (void **) &data, + &data_size))) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Attestation value invalid!\n"); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Credential value invalid!\n"); return GNUNET_SYSERR; } - attr = GNUNET_RECLAIM_attestation_new (name_str, type, data, data_size); + attr = GNUNET_RECLAIM_credential_new (name_str, type, data, data_size); if ((NULL == id_str) || (0 == strlen (id_str))) memset (&attr->id, 0, sizeof (attr->id)); else @@ -344,7 +344,7 @@ parse_attest (void *cls, json_t *root, struct GNUNET_JSON_Specification *spec) &attr->id, sizeof(attr->id)); - *(struct GNUNET_RECLAIM_Attestation **) spec->ptr = attr; + *(struct GNUNET_RECLAIM_Credential **) spec->ptr = attr; return GNUNET_OK; } @@ -356,11 +356,11 @@ parse_attest (void *cls, json_t *root, struct GNUNET_JSON_Specification *spec) * @param[out] spec where to free the data */ static void -clean_attest (void *cls, struct GNUNET_JSON_Specification *spec) +clean_credential (void *cls, struct GNUNET_JSON_Specification *spec) { - struct GNUNET_RECLAIM_Attestation **attr; + struct GNUNET_RECLAIM_Credential **attr; - attr = (struct GNUNET_RECLAIM_Attestation **) spec->ptr; + attr = (struct GNUNET_RECLAIM_Credential **) spec->ptr; if (NULL != *attr) { GNUNET_free (*attr); @@ -370,23 +370,23 @@ clean_attest (void *cls, struct GNUNET_JSON_Specification *spec) /** - * JSON Specification for Reclaim attestation claims. + * JSON Specification for credential claims. * - * @param ticket struct of GNUNET_RECLAIM_ATTESTATION_Claim to fill + * @param attr struct of GNUNET_RECLAIM_Credential to fill * @return JSON Specification */ struct GNUNET_JSON_Specification -GNUNET_RECLAIM_JSON_spec_claim_attest (struct - GNUNET_RECLAIM_Attestation **attr) +GNUNET_RECLAIM_JSON_spec_credential (struct + GNUNET_RECLAIM_Credential **cred) { - struct GNUNET_JSON_Specification ret = { .parser = &parse_attest, - .cleaner = &clean_attest, + struct GNUNET_JSON_Specification ret = { .parser = &parse_credential, + .cleaner = &clean_credential, .cls = NULL, .field = NULL, .ptr = attr, .ptr_size = 0, .size_ptr = NULL }; - *attr = NULL; + *cred = NULL; return ret; } diff --git a/src/reclaim/json_reclaim.h b/src/reclaim/json_reclaim.h index c57971dcb..8911cf92d 100644 --- a/src/reclaim/json_reclaim.h +++ b/src/reclaim/json_reclaim.h @@ -32,11 +32,11 @@ /** * JSON Specification for Reclaim claims. * - * @param ticket struct of GNUNET_RECLAIM_ATTRIBUTE_Claim to fill + * @param attr struct of GNUNET_RECLAIM_Attribute to fill * @return JSON Specification */ struct GNUNET_JSON_Specification -GNUNET_RECLAIM_JSON_spec_claim (struct GNUNET_RECLAIM_Attribute **attr); +GNUNET_RECLAIM_JSON_spec_attribute (struct GNUNET_RECLAIM_Attribute **attr); /** * JSON Specification for Reclaim tickets. @@ -48,11 +48,10 @@ struct GNUNET_JSON_Specification GNUNET_RECLAIM_JSON_spec_ticket (struct GNUNET_RECLAIM_Ticket **ticket); /** - * JSON Specification for Reclaim attestation claims. + * JSON Specification for credentials. * - * @param ticket struct of GNUNET_RECLAIM_Attestation to fill + * @param cred struct of GNUNET_RECLAIM_Credential to fill * @return JSON Specification */ struct GNUNET_JSON_Specification -GNUNET_RECLAIM_JSON_spec_claim_attest (struct - GNUNET_RECLAIM_Attestation **attr); +GNUNET_RECLAIM_JSON_spec_credential (struct GNUNET_RECLAIM_Attestation **cred); diff --git a/src/reclaim/oidc_helper.c b/src/reclaim/oidc_helper.c index eab12db33..9c7e79c41 100644 --- a/src/reclaim/oidc_helper.c +++ b/src/reclaim/oidc_helper.c @@ -62,9 +62,9 @@ struct OIDC_Parameters uint32_t attr_list_len GNUNET_PACKED; /** - * The length of the attestation list + * The length of the presentation list */ - uint32_t attest_list_len GNUNET_PACKED; + uint32_t pres_list_len GNUNET_PACKED; }; GNUNET_NETWORK_STRUCT_END @@ -157,24 +157,24 @@ fix_base64 (char *str) static json_t* generate_userinfo_json(const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, struct GNUNET_RECLAIM_AttributeList *attrs, - struct GNUNET_RECLAIM_AttestationList *attests) + struct GNUNET_RECLAIM_PresentationList *presentations) { struct GNUNET_RECLAIM_AttributeListEntry *le; - struct GNUNET_RECLAIM_AttestationListEntry *ale; + struct GNUNET_RECLAIM_PresentationListEntry *ple; char *subject; char *source_name; char *attr_val_str; - char *attest_val_str; + char *pres_val_str; json_t *body; json_t *aggr_names; json_t *aggr_sources; json_t *aggr_sources_jwt; json_t *addr_claim = NULL; - int num_attestations = 0; + int num_presentations = 0; for (le = attrs->list_head; NULL != le; le = le->next) { - if (GNUNET_NO == GNUNET_RECLAIM_id_is_zero (&le->attribute->attestation)) - num_attestations++; + if (GNUNET_NO == GNUNET_RECLAIM_id_is_zero (&le->attribute->credential)) + num_presentations++; } subject = @@ -191,23 +191,25 @@ generate_userinfo_json(const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, json_object_set_new (body, "iss", json_string (SERVER_ADDRESS)); // sub REQUIRED public key identity, not exceed 255 ASCII length json_object_set_new (body, "sub", json_string (subject)); - attest_val_str = NULL; + pres_val_str = NULL; source_name = NULL; int i = 0; - for (ale = attests->list_head; NULL != ale; ale = ale->next) + for (ple = presentations->list_head; NULL != ple; ple = ple->next) { - // New Attestation + // New presentation GNUNET_asprintf (&source_name, "src%d", i); aggr_sources_jwt = json_object (); - attest_val_str = - GNUNET_RECLAIM_attestation_value_to_string (ale->attestation->type, - ale->attestation->data, - ale->attestation->data_size); - json_object_set_new (aggr_sources_jwt, "JWT", - json_string (attest_val_str) ); + pres_val_str = + GNUNET_RECLAIM_presentation_value_to_string (ple->presentation->type, + ple->presentation->data, + ple->presentation->data_size); + json_object_set_new (aggr_sources_jwt, + GNUNET_RECLAIM_presentation_number_to_typename (ple->presentation->type), + json_string (pres_val_str) ); json_object_set_new (aggr_sources, source_name, aggr_sources_jwt); + GNUNET_free (pres_val_str); GNUNET_free (source_name); source_name = NULL; i++; @@ -216,7 +218,7 @@ generate_userinfo_json(const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, for (le = attrs->list_head; NULL != le; le = le->next) { - if (GNUNET_YES == GNUNET_RECLAIM_id_is_zero (&le->attribute->attestation)) + if (GNUNET_YES == GNUNET_RECLAIM_id_is_zero (&le->attribute->credential)) { attr_val_str = @@ -247,18 +249,24 @@ generate_userinfo_json(const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, } else { - // Check if attest is there + // Check if presentation is there int j = 0; - for (ale = attests->list_head; NULL != ale; ale = ale->next) + for (ple = presentations->list_head; NULL != ple; ple = ple->next) { if (GNUNET_YES == - GNUNET_RECLAIM_id_is_equal (&ale->attestation->id, - &le->attribute->attestation)) + GNUNET_RECLAIM_id_is_equal (&ple->presentation->credential_id, + &le->attribute->credential)) break; j++; } - GNUNET_assert (NULL != ale); - // Attestation is existing, hence take the respective source str + if (NULL == ple) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Presentation for `%s' missing...\n", + le->attribute->name); + continue; + } + // Presentation exists, hence take the respective source str GNUNET_asprintf (&source_name, "src%d", j); @@ -269,9 +277,6 @@ generate_userinfo_json(const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, } if (NULL != addr_claim) json_object_set_new (body, "address", addr_claim); - - if (NULL != attest_val_str) - GNUNET_free (attest_val_str); if (0 != i) { json_object_set_new (body, "_claim_names", aggr_names); @@ -286,18 +291,18 @@ generate_userinfo_json(const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, * * @param sub_key the subject (user) * @param attrs user attribute list - * @param attests user attribute attestation list (may be empty) + * @param presentations credential presentation list (may be empty) * @return Userinfo JSON */ char * OIDC_generate_userinfo (const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, struct GNUNET_RECLAIM_AttributeList *attrs, - struct GNUNET_RECLAIM_AttestationList *attests) + struct GNUNET_RECLAIM_PresentationList *presentations) { char *body_str; json_t* body = generate_userinfo_json (sub_key, attrs, - attests); + presentations); body_str = json_dumps (body, JSON_INDENT (0) | JSON_COMPACT); json_decref (body); return body_str; @@ -310,6 +315,7 @@ OIDC_generate_userinfo (const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, * @param aud_key the public of the audience * @param sub_key the public key of the subject * @param attrs the attribute list + * @param presentations credential presentation list (may be empty) * @param expiration_time the validity of the token * @param secret_key the key used to sign the JWT * @return a new base64-encoded JWT string. @@ -318,7 +324,7 @@ char * OIDC_generate_id_token (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, struct GNUNET_RECLAIM_AttributeList *attrs, - struct GNUNET_RECLAIM_AttestationList *attests, + struct GNUNET_RECLAIM_PresentationList *presentations, const struct GNUNET_TIME_Relative *expiration_time, const char *nonce, const char *secret_key) @@ -339,7 +345,7 @@ OIDC_generate_id_token (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, body = generate_userinfo_json (sub_key, attrs, - attests); + presentations); // iat REQUIRED time now time_now = GNUNET_TIME_absolute_get (); // exp REQUIRED time expired from config @@ -426,6 +432,7 @@ OIDC_generate_id_token (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, * @param issuer the issuer of the ticket, used to sign the ticket and nonce * @param ticket the ticket to include in the code * @param attrs list of attributes which are shared + * @param presentations credential presentation list (may be empty) * @param nonce the nonce to include in the code * @param code_challenge PKCE code challenge * @return a new authorization code (caller must free) @@ -434,7 +441,7 @@ char * OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer, const struct GNUNET_RECLAIM_Ticket *ticket, struct GNUNET_RECLAIM_AttributeList *attrs, - struct GNUNET_RECLAIM_AttestationList *attests, + struct GNUNET_RECLAIM_PresentationList *presentations, const char *nonce_str, const char *code_challenge) { @@ -447,7 +454,7 @@ OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer, size_t payload_len; size_t code_payload_len; size_t attr_list_len = 0; - size_t attests_list_len = 0; + size_t pres_list_len = 0; size_t code_challenge_len = 0; uint32_t nonce_len = 0; struct GNUNET_CRYPTO_EccSignaturePurpose *purpose; @@ -481,17 +488,17 @@ OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer, // Get serialized attributes payload_len += attr_list_len; } - if (NULL != attests) + if (NULL != presentations) { // Get length - attests_list_len = - GNUNET_RECLAIM_attestation_list_serialize_get_size (attests); - params.attest_list_len = htonl (attests_list_len); + pres_list_len = + GNUNET_RECLAIM_presentation_list_serialize_get_size (presentations); + params.pres_list_len = htonl (pres_list_len); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Length of serialized attestations: %lu\n", - attests_list_len); + "Length of serialized presentations: %lu\n", + pres_list_len); // Get serialized attributes - payload_len += attests_list_len; + payload_len += pres_list_len; } // Get plaintext length @@ -510,8 +517,8 @@ OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer, } if (0 < attr_list_len) GNUNET_RECLAIM_attribute_list_serialize (attrs, tmp); - if (0 < attests_list_len) - GNUNET_RECLAIM_attestation_list_serialize (attests, tmp); + if (0 < pres_list_len) + GNUNET_RECLAIM_presentation_list_serialize (presentations, tmp); /** END **/ @@ -564,7 +571,7 @@ OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer, * if used in request. * @param ticket where to store the ticket * @param attrs the attributes in the code - * @param attests the attestations in the code (if any) + * @param presentations credential presentation list * @param nonce_str where to store the nonce (if contained) * @return GNUNET_OK if successful, else GNUNET_SYSERR */ @@ -574,14 +581,14 @@ OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPublicKey *audience, const char *code_verifier, struct GNUNET_RECLAIM_Ticket *ticket, struct GNUNET_RECLAIM_AttributeList **attrs, - struct GNUNET_RECLAIM_AttestationList **attests, + struct GNUNET_RECLAIM_PresentationList **presentations, char **nonce_str) { char *code_payload; char *ptr; char *plaintext; char *attrs_ser; - char *attests_ser; + char *presentations_ser; char *expected_code_challenge; char *code_challenge; char *code_verifier_hash; @@ -589,7 +596,7 @@ OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPublicKey *audience, struct GNUNET_CRYPTO_EcdsaSignature *signature; uint32_t code_challenge_len; uint32_t attrs_ser_len; - uint32_t attests_ser_len; + uint32_t presentations_ser_len; size_t plaintext_len; size_t code_payload_len; uint32_t nonce_len = 0; @@ -692,10 +699,11 @@ OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPublicKey *audience, attrs_ser = ((char *) ¶ms[1]) + code_challenge_len + nonce_len; attrs_ser_len = ntohl (params->attr_list_len); *attrs = GNUNET_RECLAIM_attribute_list_deserialize (attrs_ser, attrs_ser_len); - attests_ser = ((char*) attrs_ser) + attrs_ser_len; - attests_ser_len = ntohl (params->attest_list_len); - *attests = GNUNET_RECLAIM_attestation_list_deserialize (attests_ser, - attests_ser_len); + presentations_ser = ((char*) attrs_ser) + attrs_ser_len; + pres_ser_len = ntohl (params->pres_list_len); + *presentations = + GNUNET_RECLAIM_presentations_list_deserialize (presentations_ser, + pres_ser_len); GNUNET_free (code_payload); return GNUNET_OK; diff --git a/src/reclaim/oidc_helper.h b/src/reclaim/oidc_helper.h index e713dab62..789a2acc7 100644 --- a/src/reclaim/oidc_helper.h +++ b/src/reclaim/oidc_helper.h @@ -44,6 +44,7 @@ * @param aud_key the public of the audience * @param sub_key the public key of the subject * @param attrs the attribute list + * @param presentations credential presentation list (may be empty) * @param expiration_time the validity of the token * @param secret_key the key used to sign the JWT * @return a new base64-encoded JWT string. @@ -52,7 +53,7 @@ char* OIDC_generate_id_token (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, struct GNUNET_RECLAIM_AttributeList *attrs, - struct GNUNET_RECLAIM_AttestationList *attests, + struct GNUNET_RECLAIM_PresentationList *presentations, const struct GNUNET_TIME_Relative *expiration_time, const char *nonce, const char *secret_key); @@ -64,6 +65,7 @@ OIDC_generate_id_token (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, * @param issuer the issuer of the ticket, used to sign the ticket and nonce * @param ticket the ticket to include in the code * @param attrs list of attributes to share + * @param presentations credential presentation list * @param nonce the nonce to include in the code * @param code_challenge PKCE code challenge * @return a new authorization code (caller must free) @@ -72,7 +74,7 @@ char* OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer, const struct GNUNET_RECLAIM_Ticket *ticket, struct GNUNET_RECLAIM_AttributeList *attrs, - struct GNUNET_RECLAIM_AttestationList *attests, + struct GNUNET_RECLAIM_PresentationList *presentations, const char *nonce, const char *code_challenge); @@ -86,6 +88,7 @@ OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer, * @param code_verfier PKCE code verifier * @param ticket where to store the ticket * @param attrs the attributes found in the code + * @param presentations credential presentation list * @param nonce where to store the nonce * @return GNUNET_OK if successful, else GNUNET_SYSERR */ @@ -95,7 +98,7 @@ OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPublicKey *ecdsa_pub, const char *code_verifier, struct GNUNET_RECLAIM_Ticket *ticket, struct GNUNET_RECLAIM_AttributeList **attrs, - struct GNUNET_RECLAIM_AttestationList **attests, + struct GNUNET_RECLAIM_PresentationList **presentations, char **nonce); /** @@ -145,12 +148,12 @@ OIDC_check_scopes_for_claim_request (const char*scopes, * * @param sub_key the subject (user) * @param attrs user attribute list - * @param attests user attribute attestation list (may be empty) + * @param presentations credential presentation list * @return Userinfo JSON */ char * OIDC_generate_userinfo (const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, struct GNUNET_RECLAIM_AttributeList *attrs, - struct GNUNET_RECLAIM_AttestationList *attests); + struct GNUNET_RECLAIM_PresentationList *presentations); #endif diff --git a/src/reclaim/plugin_reclaim_attestation_jwt.c b/src/reclaim/plugin_reclaim_attestation_jwt.c deleted file mode 100644 index c87d3e61a..000000000 --- a/src/reclaim/plugin_reclaim_attestation_jwt.c +++ /dev/null @@ -1,321 +0,0 @@ -/* - This file is part of GNUnet - Copyright (C) 2013, 2014, 2016 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - 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 - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file reclaim-attribute/plugin_reclaim_attestation_gnuid.c - * @brief reclaim-attribute-plugin-gnuid attribute plugin to provide the API for - * fundamental - * attribute types. - * - * @author Martin Schanzenbach - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_reclaim_plugin.h" -#include -#include - -/** - * Convert the 'value' of an attestation to a string. - * - * @param cls closure, unused - * @param type type of the attestation - * @param data value in binary encoding - * @param data_size number of bytes in @a data - * @return NULL on error, otherwise human-readable representation of the value - */ -static char * -jwt_value_to_string (void *cls, - uint32_t type, - const void *data, - size_t data_size) -{ - switch (type) - { - case GNUNET_RECLAIM_ATTESTATION_TYPE_JWT: - return GNUNET_strndup (data, data_size); - - default: - return NULL; - } -} - - -/** - * Convert human-readable version of a 'value' of an attestation to the binary - * representation. - * - * @param cls closure, unused - * @param type type of the attestation - * @param s human-readable string - * @param data set to value in binary encoding (will be allocated) - * @param data_size set to number of bytes in @a data - * @return #GNUNET_OK on success - */ -static int -jwt_string_to_value (void *cls, - uint32_t type, - const char *s, - void **data, - size_t *data_size) -{ - if (NULL == s) - return GNUNET_SYSERR; - switch (type) - { - case GNUNET_RECLAIM_ATTESTATION_TYPE_JWT: - *data = GNUNET_strdup (s); - *data_size = strlen (s); - return GNUNET_OK; - - default: - return GNUNET_SYSERR; - } -} - - -/** - * Mapping of attestation type numbers to human-readable - * attestation type names. - */ -static struct -{ - const char *name; - uint32_t number; -} jwt_attest_name_map[] = { { "JWT", GNUNET_RECLAIM_ATTESTATION_TYPE_JWT }, - { NULL, UINT32_MAX } }; - -/** - * Convert a type name to the corresponding number. - * - * @param cls closure, unused - * @param jwt_typename name to convert - * @return corresponding number, UINT32_MAX on error - */ -static uint32_t -jwt_typename_to_number (void *cls, const char *jwt_typename) -{ - unsigned int i; - - i = 0; - while ((NULL != jwt_attest_name_map[i].name) && - (0 != strcasecmp (jwt_typename, jwt_attest_name_map[i].name))) - i++; - return jwt_attest_name_map[i].number; -} - - -/** - * Convert a type number (i.e. 1) to the corresponding type string - * - * @param cls closure, unused - * @param type number of a type to convert - * @return corresponding typestring, NULL on error - */ -static const char * -jwt_number_to_typename (void *cls, uint32_t type) -{ - unsigned int i; - - i = 0; - while ((NULL != jwt_attest_name_map[i].name) && (type != - jwt_attest_name_map[i]. - number)) - i++; - return jwt_attest_name_map[i].name; -} - - -/** - * Parse a JWT and return the respective claim value as Attribute - * - * @param cls the plugin - * @param attest the jwt attestation - * @return a GNUNET_RECLAIM_Attribute, containing the new value - */ -struct GNUNET_RECLAIM_AttributeList * -jwt_parse_attributes (void *cls, - const struct GNUNET_RECLAIM_Attestation *attest) -{ - char *jwt_string; - struct GNUNET_RECLAIM_AttributeList *attrs; - char delim[] = "."; - char *val_str = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Parsing JWT attributes.\n"); - char *decoded_jwt; - json_t *json_val; - json_error_t *json_err = NULL; - - /* GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "%s\n", attest->data); (not OK: 'data' is not defined as 0-terminated text, but binary) */ - if (GNUNET_RECLAIM_ATTESTATION_TYPE_JWT != attest->type) - return NULL; - attrs = GNUNET_new (struct GNUNET_RECLAIM_AttributeList); - - jwt_string = GNUNET_strdup (attest->data); - const char *jwt_body = strtok (jwt_string, delim); - jwt_body = strtok (NULL, delim); - GNUNET_STRINGS_base64url_decode (jwt_body, strlen (jwt_body), - (void **) &decoded_jwt); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n", decoded_jwt); - GNUNET_assert (NULL != decoded_jwt); - json_val = json_loads (decoded_jwt, JSON_DECODE_ANY, json_err); - const char *key; - json_t *value; - json_object_foreach (json_val, key, value) { - if (0 == strcmp ("iss", key)) - continue; - if (0 == strcmp ("exp", key)) - continue; - if (0 == strcmp ("iat", key)) - continue; - if (0 == strcmp ("nbf", key)) - continue; - if (0 == strcmp ("aud", key)) - continue; - val_str = json_dumps (value, JSON_ENCODE_ANY); - GNUNET_RECLAIM_attribute_list_add (attrs, - key, - NULL, - GNUNET_RECLAIM_ATTRIBUTE_TYPE_STRING,// FIXME - val_str, - strlen (val_str)); - GNUNET_free (val_str); - } - GNUNET_free (jwt_string); - return attrs; -} - - -/** - * Parse a JWT and return the issuer - * - * @param cls the plugin - * @param attest the jwt attestation - * @return a string, containing the isser - */ -char * -jwt_get_issuer (void *cls, - const struct GNUNET_RECLAIM_Attestation *attest) -{ - const char *jwt_body; - char *jwt_string; - char delim[] = "."; - char *issuer = NULL; - char *decoded_jwt; - json_t *issuer_json; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Parsing JWT attributes.\n"); - json_t *json_val; - json_error_t *json_err = NULL; - - if (GNUNET_RECLAIM_ATTESTATION_TYPE_JWT != attest->type) - return NULL; - jwt_string = GNUNET_strdup (attest->data); - jwt_body = strtok (jwt_string, delim); - jwt_body = strtok (NULL, delim); - GNUNET_STRINGS_base64url_decode (jwt_body, strlen (jwt_body), - (void **) &decoded_jwt); - json_val = json_loads (decoded_jwt, JSON_DECODE_ANY, json_err); - issuer_json = json_object_get (json_val, "iss"); - if ((NULL == issuer_json) || (! json_is_string (issuer_json))) - return NULL; - issuer = GNUNET_strdup (json_string_value (issuer_json)); - GNUNET_free (jwt_string); - return issuer; -} - - -/** - * Parse a JWT and return the expiration - * - * @param cls the plugin - * @param attest the jwt attestation - * @return a string, containing the isser - */ -int -jwt_get_expiration (void *cls, - const struct GNUNET_RECLAIM_Attestation *attest, - struct GNUNET_TIME_Absolute *exp) -{ - const char *jwt_body; - char *jwt_string; - char delim[] = "."; - char *decoded_jwt; - json_t *exp_json; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Parsing JWT attributes.\n"); - json_t *json_val; - json_error_t *json_err = NULL; - - if (GNUNET_RECLAIM_ATTESTATION_TYPE_JWT != attest->type) - return GNUNET_NO; - jwt_string = GNUNET_strdup (attest->data); - jwt_body = strtok (jwt_string, delim); - jwt_body = strtok (NULL, delim); - GNUNET_STRINGS_base64url_decode (jwt_body, strlen (jwt_body), - (void **) &decoded_jwt); - json_val = json_loads (decoded_jwt, JSON_DECODE_ANY, json_err); - exp_json = json_object_get (json_val, "exp"); - if ((NULL == exp_json) || (! json_is_integer (exp_json))) - return GNUNET_SYSERR; - exp->abs_value_us = json_integer_value (exp_json) * 1000 * 1000; - GNUNET_free (jwt_string); - return GNUNET_OK; -} - - -/** - * Entry point for the plugin. - * - * @param cls NULL - * @return the exported block API - */ -void * -libgnunet_plugin_reclaim_attestation_jwt_init (void *cls) -{ - struct GNUNET_RECLAIM_AttestationPluginFunctions *api; - - api = GNUNET_new (struct GNUNET_RECLAIM_AttestationPluginFunctions); - api->value_to_string = &jwt_value_to_string; - api->string_to_value = &jwt_string_to_value; - api->typename_to_number = &jwt_typename_to_number; - api->number_to_typename = &jwt_number_to_typename; - api->get_attributes = &jwt_parse_attributes; - api->get_issuer = &jwt_get_issuer; - api->get_expiration = &jwt_get_expiration; - return api; -} - - -/** - * Exit point from the plugin. - * - * @param cls the return value from #libgnunet_plugin_block_test_init() - * @return NULL - */ -void * -libgnunet_plugin_reclaim_attestation_jwt_done (void *cls) -{ - struct GNUNET_RECLAIM_AttestationPluginFunctions *api = cls; - - GNUNET_free (api); - return NULL; -} - - -/* end of plugin_reclaim_attestation_type_gnuid.c */ diff --git a/src/reclaim/plugin_reclaim_credential_jwt.c b/src/reclaim/plugin_reclaim_credential_jwt.c new file mode 100644 index 000000000..38effcf78 --- /dev/null +++ b/src/reclaim/plugin_reclaim_credential_jwt.c @@ -0,0 +1,320 @@ +/* + This file is part of GNUnet + Copyright (C) 2013, 2014, 2016 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file reclaim/plugin_reclaim_credential_jwt.c + * @brief reclaim-credential-plugin-jwt attribute plugin to provide the API for + * JWT credentials. + * + * @author Martin Schanzenbach + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_reclaim_plugin.h" +#include +#include + +/** + * Convert the 'value' of an credential to a string. + * + * @param cls closure, unused + * @param type type of the credential + * @param data value in binary encoding + * @param data_size number of bytes in @a data + * @return NULL on error, otherwise human-readable representation of the value + */ +static char * +jwt_value_to_string (void *cls, + uint32_t type, + const void *data, + size_t data_size) +{ + switch (type) + { + case GNUNET_RECLAIM_credential_TYPE_JWT: + return GNUNET_strndup (data, data_size); + + default: + return NULL; + } +} + + +/** + * Convert human-readable version of a 'value' of an credential to the binary + * representation. + * + * @param cls closure, unused + * @param type type of the credential + * @param s human-readable string + * @param data set to value in binary encoding (will be allocated) + * @param data_size set to number of bytes in @a data + * @return #GNUNET_OK on success + */ +static int +jwt_string_to_value (void *cls, + uint32_t type, + const char *s, + void **data, + size_t *data_size) +{ + if (NULL == s) + return GNUNET_SYSERR; + switch (type) + { + case GNUNET_RECLAIM_credential_TYPE_JWT: + *data = GNUNET_strdup (s); + *data_size = strlen (s); + return GNUNET_OK; + + default: + return GNUNET_SYSERR; + } +} + + +/** + * Mapping of credential type numbers to human-readable + * credential type names. + */ +static struct +{ + const char *name; + uint32_t number; +} jwt_cred_name_map[] = { { "JWT", GNUNET_RECLAIM_credential_TYPE_JWT }, + { NULL, UINT32_MAX } }; + +/** + * Convert a type name to the corresponding number. + * + * @param cls closure, unused + * @param jwt_typename name to convert + * @return corresponding number, UINT32_MAX on error + */ +static uint32_t +jwt_typename_to_number (void *cls, const char *jwt_typename) +{ + unsigned int i; + + i = 0; + while ((NULL != jwt_cred_name_map[i].name) && + (0 != strcasecmp (jwt_typename, jwt_cred_name_map[i].name))) + i++; + return jwt_cred_name_map[i].number; +} + + +/** + * Convert a type number (i.e. 1) to the corresponding type string + * + * @param cls closure, unused + * @param type number of a type to convert + * @return corresponding typestring, NULL on error + */ +static const char * +jwt_number_to_typename (void *cls, uint32_t type) +{ + unsigned int i; + + i = 0; + while ((NULL != jwt_cred_name_map[i].name) && (type != + jwt_cred_name_map[i]. + number)) + i++; + return jwt_cred_name_map[i].name; +} + + +/** + * Parse a JWT and return the respective claim value as Attribute + * + * @param cls the plugin + * @param cred the jwt credential + * @return a GNUNET_RECLAIM_Attribute, containing the new value + */ +struct GNUNET_RECLAIM_AttributeList * +jwt_parse_attributes (void *cls, + const struct GNUNET_RECLAIM_Credential *cred) +{ + char *jwt_string; + struct GNUNET_RECLAIM_AttributeList *attrs; + char delim[] = "."; + char *val_str = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Parsing JWT attributes.\n"); + char *decoded_jwt; + json_t *json_val; + json_error_t *json_err = NULL; + + /* GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "%s\n", cred->data); (not OK: 'data' is not defined as 0-terminated text, but binary) */ + if (GNUNET_RECLAIM_credential_TYPE_JWT != cred->type) + return NULL; + attrs = GNUNET_new (struct GNUNET_RECLAIM_AttributeList); + + jwt_string = GNUNET_strdup (cred->data); + const char *jwt_body = strtok (jwt_string, delim); + jwt_body = strtok (NULL, delim); + GNUNET_STRINGS_base64url_decode (jwt_body, strlen (jwt_body), + (void **) &decoded_jwt); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n", decoded_jwt); + GNUNET_assert (NULL != decoded_jwt); + json_val = json_loads (decoded_jwt, JSON_DECODE_ANY, json_err); + const char *key; + json_t *value; + json_object_foreach (json_val, key, value) { + if (0 == strcmp ("iss", key)) + continue; + if (0 == strcmp ("exp", key)) + continue; + if (0 == strcmp ("iat", key)) + continue; + if (0 == strcmp ("nbf", key)) + continue; + if (0 == strcmp ("aud", key)) + continue; + val_str = json_dumps (value, JSON_ENCODE_ANY); + GNUNET_RECLAIM_attribute_list_add (attrs, + key, + NULL, + GNUNET_RECLAIM_ATTRIBUTE_TYPE_STRING,// FIXME + val_str, + strlen (val_str)); + GNUNET_free (val_str); + } + GNUNET_free (jwt_string); + return attrs; +} + + +/** + * Parse a JWT and return the issuer + * + * @param cls the plugin + * @param cred the jwt credential + * @return a string, containing the isser + */ +char * +jwt_get_issuer (void *cls, + const struct GNUNET_RECLAIM_Credential *cred) +{ + const char *jwt_body; + char *jwt_string; + char delim[] = "."; + char *issuer = NULL; + char *decoded_jwt; + json_t *issuer_json; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Parsing JWT attributes.\n"); + json_t *json_val; + json_error_t *json_err = NULL; + + if (GNUNET_RECLAIM_credential_TYPE_JWT != cred->type) + return NULL; + jwt_string = GNUNET_strdup (cred->data); + jwt_body = strtok (jwt_string, delim); + jwt_body = strtok (NULL, delim); + GNUNET_STRINGS_base64url_decode (jwt_body, strlen (jwt_body), + (void **) &decoded_jwt); + json_val = json_loads (decoded_jwt, JSON_DECODE_ANY, json_err); + issuer_json = json_object_get (json_val, "iss"); + if ((NULL == issuer_json) || (! json_is_string (issuer_json))) + return NULL; + issuer = GNUNET_strdup (json_string_value (issuer_json)); + GNUNET_free (jwt_string); + return issuer; +} + + +/** + * Parse a JWT and return the expiration + * + * @param cls the plugin + * @param cred the jwt credential + * @return a string, containing the isser + */ +int +jwt_get_expiration (void *cls, + const struct GNUNET_RECLAIM_Credential *cred, + struct GNUNET_TIME_Absolute *exp) +{ + const char *jwt_body; + char *jwt_string; + char delim[] = "."; + char *decoded_jwt; + json_t *exp_json; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Parsing JWT attributes.\n"); + json_t *json_val; + json_error_t *json_err = NULL; + + if (GNUNET_RECLAIM_credential_TYPE_JWT != cred->type) + return GNUNET_NO; + jwt_string = GNUNET_strdup (cred->data); + jwt_body = strtok (jwt_string, delim); + jwt_body = strtok (NULL, delim); + GNUNET_STRINGS_base64url_decode (jwt_body, strlen (jwt_body), + (void **) &decoded_jwt); + json_val = json_loads (decoded_jwt, JSON_DECODE_ANY, json_err); + exp_json = json_object_get (json_val, "exp"); + if ((NULL == exp_json) || (! json_is_integer (exp_json))) + return GNUNET_SYSERR; + exp->abs_value_us = json_integer_value (exp_json) * 1000 * 1000; + GNUNET_free (jwt_string); + return GNUNET_OK; +} + + +/** + * Entry point for the plugin. + * + * @param cls NULL + * @return the exported block API + */ +void * +libgnunet_plugin_reclaim_credential_jwt_init (void *cls) +{ + struct GNUNET_RECLAIM_CredentialPluginFunctions *api; + + api = GNUNET_new (struct GNUNET_RECLAIM_CredentialPluginFunctions); + api->value_to_string = &jwt_value_to_string; + api->string_to_value = &jwt_string_to_value; + api->typename_to_number = &jwt_typename_to_number; + api->number_to_typename = &jwt_number_to_typename; + api->get_attributes = &jwt_parse_attributes; + api->get_issuer = &jwt_get_issuer; + api->get_expiration = &jwt_get_expiration; + return api; +} + + +/** + * Exit point from the plugin. + * + * @param cls the return value from #libgnunet_plugin_block_test_init() + * @return NULL + */ +void * +libgnunet_plugin_reclaim_credential_jwt_done (void *cls) +{ + struct GNUNET_RECLAIM_CredentialPluginFunctions *api = cls; + + GNUNET_free (api); + return NULL; +} + + +/* end of plugin_reclaim_credential_type_jwt.c */ diff --git a/src/reclaim/plugin_rest_openid_connect.c b/src/reclaim/plugin_rest_openid_connect.c index 2f44917c9..5ae6565af 100644 --- a/src/reclaim/plugin_rest_openid_connect.c +++ b/src/reclaim/plugin_rest_openid_connect.c @@ -439,9 +439,9 @@ struct RequestHandle struct GNUNET_RECLAIM_AttributeList *attr_userinfo_list; /** - * Attestation list + * Credential list */ - struct GNUNET_RECLAIM_AttestationList *attests_list; + struct GNUNET_RECLAIM_CredentialList *creds_list; /** @@ -461,9 +461,9 @@ struct RequestHandle struct GNUNET_RECLAIM_AttributeIterator *attr_it; /** - * Attestation iterator + * Credential iterator */ - struct GNUNET_RECLAIM_AttestationIterator *attest_it; + struct GNUNET_RECLAIM_CredentialIterator *cred_it; /** @@ -561,8 +561,8 @@ cleanup_handle (struct RequestHandle *handle) GNUNET_SCHEDULER_cancel (handle->timeout_task); if (NULL != handle->attr_it) GNUNET_RECLAIM_get_attributes_stop (handle->attr_it); - if (NULL != handle->attest_it) - GNUNET_RECLAIM_get_attestations_stop (handle->attest_it); + if (NULL != handle->cred_it) + GNUNET_RECLAIM_get_credentials_stop (handle->cred_it); if (NULL != handle->ticket_it) GNUNET_RECLAIM_ticket_iteration_stop (handle->ticket_it); if (NULL != handle->idp_op) @@ -590,8 +590,8 @@ cleanup_handle (struct RequestHandle *handle) GNUNET_RECLAIM_attribute_list_destroy (handle->attr_idtoken_list); if (NULL!=handle->attr_userinfo_list) GNUNET_RECLAIM_attribute_list_destroy (handle->attr_userinfo_list); - if (NULL!=handle->attests_list) - GNUNET_RECLAIM_attestation_list_destroy (handle->attests_list); + if (NULL!=handle->creds_list) + GNUNET_RECLAIM_credential_list_destroy (handle->creds_list); GNUNET_CONTAINER_DLL_remove (requests_head, requests_tail, handle); @@ -957,7 +957,7 @@ oidc_ticket_issue_cb (void *cls, const struct GNUNET_RECLAIM_Ticket *ticket) code_string = OIDC_build_authz_code (&handle->priv_key, &handle->ticket, handle->attr_idtoken_list, - handle->attests_list, + handle->creds_list, handle->oidc->nonce, handle->oidc->code_challenge); if ((NULL != handle->redirect_prefix) && (NULL != handle->redirect_suffix) && @@ -1010,13 +1010,13 @@ attribute_list_merge (struct GNUNET_RECLAIM_AttributeList *list_a, le_m = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry); le_m->attribute = GNUNET_RECLAIM_attribute_new (le_a->attribute->name, &le_a->attribute-> - attestation, + credential, le_a->attribute->type, le_a->attribute->data, le_a->attribute->data_size); le_m->attribute->id = le_a->attribute->id; le_m->attribute->flag = le_a->attribute->flag; - le_m->attribute->attestation = le_a->attribute->attestation; + le_m->attribute->credential = le_a->attribute->credential; GNUNET_CONTAINER_DLL_insert (merged_list->list_head, merged_list->list_tail, le_m); @@ -1035,13 +1035,13 @@ attribute_list_merge (struct GNUNET_RECLAIM_AttributeList *list_a, le_m = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry); le_m->attribute = GNUNET_RECLAIM_attribute_new (le_b->attribute->name, &le_b->attribute-> - attestation, + credential, le_b->attribute->type, le_b->attribute->data, le_b->attribute->data_size); le_m->attribute->id = le_b->attribute->id; le_m->attribute->flag = le_b->attribute->flag; - le_m->attribute->attestation = le_b->attribute->attestation; + le_m->attribute->credential = le_b->attribute->credential; GNUNET_CONTAINER_DLL_insert (merged_list->list_head, merged_list->list_tail, le_m); @@ -1051,13 +1051,13 @@ attribute_list_merge (struct GNUNET_RECLAIM_AttributeList *list_a, static void -oidc_attest_collect_finished_cb (void *cls) +oidc_cred_collect_finished_cb (void *cls) { struct RequestHandle *handle = cls; struct GNUNET_RECLAIM_AttributeList *merged_list; struct GNUNET_RECLAIM_AttributeListEntry *le_m; - handle->attest_it = NULL; + handle->cred_it = NULL; merged_list = attribute_list_merge (handle->attr_idtoken_list, handle->attr_userinfo_list); for (le_m = merged_list->list_head; NULL != le_m; le_m = le_m->next) @@ -1078,40 +1078,40 @@ oidc_attest_collect_finished_cb (void *cls) * Collects all attributes for an ego if in scope parameter */ static void -oidc_attest_collect (void *cls, - const struct GNUNET_CRYPTO_EcdsaPublicKey *identity, - const struct GNUNET_RECLAIM_Attestation *attest) +oidc_cred_collect (void *cls, + const struct GNUNET_CRYPTO_EcdsaPublicKey *identity, + const struct GNUNET_RECLAIM_Credential *cred) { struct RequestHandle *handle = cls; struct GNUNET_RECLAIM_AttributeListEntry *le; - struct GNUNET_RECLAIM_AttestationListEntry *ale; + struct GNUNET_RECLAIM_CredentialListEntry *ale; - for (ale = handle->attests_list->list_head; NULL != ale; ale = ale->next) + for (ale = handle->creds_list->list_head; NULL != ale; ale = ale->next) { - if (GNUNET_NO == GNUNET_RECLAIM_id_is_equal (&ale->attestation->id, - &attest->id)) + if (GNUNET_NO == GNUNET_RECLAIM_id_is_equal (&ale->credential->id, + &cred->id)) continue; - /** Attestation already in list **/ - GNUNET_RECLAIM_get_attestations_next (handle->attest_it); + /** Credential already in list **/ + GNUNET_RECLAIM_get_credentials_next (handle->cred_it); return; } for (le = handle->attr_idtoken_list->list_head; NULL != le; le = le->next) { - if (GNUNET_NO == GNUNET_RECLAIM_id_is_equal (&le->attribute->attestation, - &attest->id)) + if (GNUNET_NO == GNUNET_RECLAIM_id_is_equal (&le->attribute->credential, + &cred->id)) continue; - /** Attestation matches for attribute, add **/ - ale = GNUNET_new (struct GNUNET_RECLAIM_AttestationListEntry); - ale->attestation = GNUNET_RECLAIM_attestation_new (attest->name, - attest->type, - attest->data, - attest->data_size); - GNUNET_CONTAINER_DLL_insert (handle->attests_list->list_head, - handle->attests_list->list_tail, + /** Credential matches for attribute, add **/ + ale = GNUNET_new (struct GNUNET_RECLAIM_CredentialListEntry); + ale->credential = GNUNET_RECLAIM_credential_new (cred->name, + cred->type, + cred->data, + cred->data_size); + GNUNET_CONTAINER_DLL_insert (handle->creds_list->list_head, + handle->creds_list->list_tail, ale); } - GNUNET_RECLAIM_get_attestations_next (handle->attest_it); + GNUNET_RECLAIM_get_credentials_next (handle->cred_it); } @@ -1129,16 +1129,16 @@ oidc_attr_collect_finished_cb (void *cls) GNUNET_SCHEDULER_add_now (&do_redirect_error, handle); return; } - handle->attests_list = GNUNET_new (struct GNUNET_RECLAIM_AttestationList); - handle->attest_it = - GNUNET_RECLAIM_get_attestations_start (idp, - &handle->priv_key, - &oidc_iteration_error, - handle, - &oidc_attest_collect, - handle, - &oidc_attest_collect_finished_cb, - handle); + handle->creds_list = GNUNET_new (struct GNUNET_RECLAIM_CredentialList); + handle->cred_it = + GNUNET_RECLAIM_get_credentials_start (idp, + &handle->priv_key, + &oidc_iteration_error, + handle, + &oidc_cred_collect, + handle, + &oidc_cred_collect_finished_cb, + handle); } @@ -1212,13 +1212,13 @@ oidc_attr_collect (void *cls, { le = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry); le->attribute = GNUNET_RECLAIM_attribute_new (attr->name, - &attr->attestation, + &attr->credential, attr->type, attr->data, attr->data_size); le->attribute->id = attr->id; le->attribute->flag = attr->flag; - le->attribute->attestation = attr->attestation; + le->attribute->credential = attr->credential; GNUNET_CONTAINER_DLL_insert (handle->attr_idtoken_list->list_head, handle->attr_idtoken_list->list_tail, le); @@ -1227,13 +1227,13 @@ oidc_attr_collect (void *cls, { le = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry); le->attribute = GNUNET_RECLAIM_attribute_new (attr->name, - &attr->attestation, + &attr->credential, attr->type, attr->data, attr->data_size); le->attribute->id = attr->id; le->attribute->flag = attr->flag; - le->attribute->attestation = attr->attestation; + le->attribute->credential = attr->credential; GNUNET_CONTAINER_DLL_insert (handle->attr_userinfo_list->list_head, handle->attr_userinfo_list->list_tail, le); @@ -1982,7 +1982,7 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle, const struct EgoEntry *ego_entry; struct GNUNET_TIME_Relative expiration_time; struct GNUNET_RECLAIM_AttributeList *cl = NULL; - struct GNUNET_RECLAIM_AttestationList *al = NULL; + struct GNUNET_RECLAIM_CredentialList *al = NULL; struct GNUNET_RECLAIM_Ticket ticket; struct GNUNET_CRYPTO_EcdsaPublicKey cid; struct GNUNET_HashCode cache_key; @@ -2124,7 +2124,7 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle, MHD_add_response_header (resp, "Content-Type", "application/json"); handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); GNUNET_RECLAIM_attribute_list_destroy (cl); - GNUNET_RECLAIM_attestation_list_destroy (al); + GNUNET_RECLAIM_credential_list_destroy (al); GNUNET_free (access_token); GNUNET_free (json_response); GNUNET_free (id_token); @@ -2139,11 +2139,11 @@ static void consume_ticket (void *cls, const struct GNUNET_CRYPTO_EcdsaPublicKey *identity, const struct GNUNET_RECLAIM_Attribute *attr, - const struct GNUNET_RECLAIM_Attestation *attest) + const struct GNUNET_RECLAIM_Credential *cred) { struct RequestHandle *handle = cls; struct GNUNET_RECLAIM_AttributeListEntry *ale; - struct GNUNET_RECLAIM_AttestationListEntry *atle; + struct GNUNET_RECLAIM_CredentialListEntry *atle; struct MHD_Response *resp; char *result_str; handle->idp_op = NULL; @@ -2152,7 +2152,7 @@ consume_ticket (void *cls, { result_str = OIDC_generate_userinfo (&handle->ticket.identity, handle->attr_userinfo_list, - handle->attests_list); + handle->creds_list); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Userinfo: %s\n", result_str); resp = GNUNET_REST_create_response (result_str); handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); @@ -2162,35 +2162,35 @@ consume_ticket (void *cls, } ale = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry); ale->attribute = GNUNET_RECLAIM_attribute_new (attr->name, - &attr->attestation, + &attr->credential, attr->type, attr->data, attr->data_size); ale->attribute->id = attr->id; ale->attribute->flag = attr->flag; - ale->attribute->attestation = attr->attestation; + ale->attribute->credential = attr->credential; GNUNET_CONTAINER_DLL_insert (handle->attr_userinfo_list->list_head, handle->attr_userinfo_list->list_tail, ale); - if (NULL == attest) + if (NULL == cred) return; - for (atle = handle->attests_list->list_head; NULL != atle; atle = atle->next) + for (atle = handle->creds_list->list_head; NULL != atle; atle = atle->next) { - if (GNUNET_NO == GNUNET_RECLAIM_id_is_equal (&atle->attestation->id, - &attest->id)) + if (GNUNET_NO == GNUNET_RECLAIM_id_is_equal (&atle->credential->id, + &cred->id)) continue; break; /** already in list **/ } if (NULL == atle) { - /** Attestation matches for attribute, add **/ - atle = GNUNET_new (struct GNUNET_RECLAIM_AttestationListEntry); - atle->attestation = GNUNET_RECLAIM_attestation_new (attest->name, - attest->type, - attest->data, - attest->data_size); - GNUNET_CONTAINER_DLL_insert (handle->attests_list->list_head, - handle->attests_list->list_tail, + /** Credential matches for attribute, add **/ + atle = GNUNET_new (struct GNUNET_RECLAIM_CredentialListEntry); + atle->credential = GNUNET_RECLAIM_credential_new (cred->name, + cred->type, + cred->data, + cred->data_size); + GNUNET_CONTAINER_DLL_insert (handle->creds_list->list_head, + handle->creds_list->list_tail, atle); } } @@ -2289,8 +2289,8 @@ userinfo_endpoint (struct GNUNET_REST_RequestHandle *con_handle, privkey = GNUNET_IDENTITY_ego_get_private_key (aud_ego->ego); handle->attr_userinfo_list = GNUNET_new (struct GNUNET_RECLAIM_AttributeList); - handle->attests_list = - GNUNET_new (struct GNUNET_RECLAIM_AttestationList); + handle->creds_list = + GNUNET_new (struct GNUNET_RECLAIM_CredentialList); handle->idp_op = GNUNET_RECLAIM_ticket_consume (idp, privkey, @@ -2611,6 +2611,7 @@ libgnunet_plugin_rest_openid_connect_init (void *cls) return api; } + static int cleanup_hashmap (void *cls, const struct GNUNET_HashCode *key, void *value) { @@ -2618,6 +2619,7 @@ cleanup_hashmap (void *cls, const struct GNUNET_HashCode *key, void *value) return GNUNET_YES; } + /** * Exit point from the plugin. * diff --git a/src/reclaim/plugin_rest_reclaim.c b/src/reclaim/plugin_rest_reclaim.c index 870baa7f3..a51d992e1 100644 --- a/src/reclaim/plugin_rest_reclaim.c +++ b/src/reclaim/plugin_rest_reclaim.c @@ -48,9 +48,9 @@ #define GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES "/reclaim/attributes" /** - * Attestation namespace + * Credential namespace */ -#define GNUNET_REST_API_NS_RECLAIM_ATTESTATION "/reclaim/attestation" +#define GNUNET_REST_API_NS_RECLAIM_CREDENTIAL "/reclaim/credential" /** * Ticket namespace @@ -202,7 +202,7 @@ struct RequestHandle /** * Attribute iterator */ - struct GNUNET_RECLAIM_AttestationIterator *attest_it; + struct GNUNET_RECLAIM_CredentialIterator *cred_it; /** * Ticket iterator @@ -282,8 +282,8 @@ cleanup_handle (void *cls) GNUNET_SCHEDULER_cancel (handle->timeout_task); if (NULL != handle->attr_it) GNUNET_RECLAIM_get_attributes_stop (handle->attr_it); - if (NULL != handle->attest_it) - GNUNET_RECLAIM_get_attestations_stop (handle->attest_it); + if (NULL != handle->cred_it) + GNUNET_RECLAIM_get_credentials_stop (handle->cred_it); if (NULL != handle->ticket_it) GNUNET_RECLAIM_ticket_iteration_stop (handle->ticket_it); if (NULL != handle->url) @@ -413,7 +413,7 @@ collect_finished_cb (void *cls) // Done handle->attr_it = NULL; - handle->attest_it = NULL; + handle->cred_it = NULL; handle->ticket_it = NULL; GNUNET_SCHEDULER_add_now (&return_response, handle); } @@ -460,7 +460,7 @@ ticket_collect (void *cls, const struct GNUNET_RECLAIM_Ticket *ticket) static void -add_attestation_cont (struct GNUNET_REST_RequestHandle *con_handle, +add_credential_cont (struct GNUNET_REST_RequestHandle *con_handle, const char *url, void *cls) { @@ -468,19 +468,19 @@ add_attestation_cont (struct GNUNET_REST_RequestHandle *con_handle, const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv; const char *identity; struct EgoEntry *ego_entry; - struct GNUNET_RECLAIM_Attestation *attribute; + struct GNUNET_RECLAIM_Credential *attribute; struct GNUNET_TIME_Relative exp; char term_data[handle->rest_handle->data_size + 1]; json_t *data_json; json_error_t err; struct GNUNET_JSON_Specification attrspec[] = - { GNUNET_RECLAIM_JSON_spec_claim_attest (&attribute), + { GNUNET_RECLAIM_JSON_spec_claim_cred (&attribute), GNUNET_JSON_spec_end () }; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Adding an attestation for %s.\n", + "Adding an credential for %s.\n", handle->url); - if (strlen (GNUNET_REST_API_NS_RECLAIM_ATTESTATION) >= strlen ( + if (strlen (GNUNET_REST_API_NS_RECLAIM_CREDENTIAL) >= strlen ( handle->url)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n"); @@ -488,7 +488,7 @@ add_attestation_cont (struct GNUNET_REST_RequestHandle *con_handle, return; } identity = handle->url + strlen ( - GNUNET_REST_API_NS_RECLAIM_ATTESTATION) + 1; + GNUNET_REST_API_NS_RECLAIM_CREDENTIAL) + 1; for (ego_entry = ego_head; NULL != ego_entry; ego_entry = ego_entry->next) @@ -518,7 +518,7 @@ add_attestation_cont (struct GNUNET_REST_RequestHandle *con_handle, if (NULL == attribute) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unable to parse attestation from %s\n", + "Unable to parse credential from %s\n", term_data); GNUNET_SCHEDULER_add_now (&do_error, handle); return; @@ -529,7 +529,7 @@ add_attestation_cont (struct GNUNET_REST_RequestHandle *con_handle, if (GNUNET_YES == GNUNET_RECLAIM_id_is_zero (&attribute->id)) GNUNET_RECLAIM_id_generate (&attribute->id); exp = GNUNET_TIME_UNIT_HOURS; - handle->idp_op = GNUNET_RECLAIM_attestation_store (idp, + handle->idp_op = GNUNET_RECLAIM_credential_store (idp, identity_priv, attribute, &exp, @@ -540,52 +540,52 @@ add_attestation_cont (struct GNUNET_REST_RequestHandle *con_handle, /** - * Collect all attestations for an ego + * Collect all credentials for an ego * */ static void -attest_collect (void *cls, +cred_collect (void *cls, const struct GNUNET_CRYPTO_EcdsaPublicKey *identity, - const struct GNUNET_RECLAIM_Attestation *attest) + const struct GNUNET_RECLAIM_Credential *cred) { struct RequestHandle *handle = cls; struct GNUNET_RECLAIM_AttributeList *attrs; struct GNUNET_RECLAIM_AttributeListEntry *ale; struct GNUNET_TIME_Absolute exp; json_t *attr_obj; - json_t *attest_obj; + json_t *cred_obj; const char *type; char *tmp_value; char *id_str; char *issuer; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attestation: %s\n", - attest->name); - attrs = GNUNET_RECLAIM_attestation_get_attributes (attest); - issuer = GNUNET_RECLAIM_attestation_get_issuer (attest); - tmp_value = GNUNET_RECLAIM_attestation_value_to_string (attest->type, - attest->data, - attest->data_size); - attest_obj = json_object (); - json_object_set_new (attest_obj, "value", json_string (tmp_value)); - json_object_set_new (attest_obj, "name", json_string (attest->name)); - type = GNUNET_RECLAIM_attestation_number_to_typename (attest->type); - json_object_set_new (attest_obj, "type", json_string (type)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding credential: %s\n", + cred->name); + attrs = GNUNET_RECLAIM_credential_get_attributes (cred); + issuer = GNUNET_RECLAIM_credential_get_issuer (cred); + tmp_value = GNUNET_RECLAIM_credential_value_to_string (cred->type, + cred->data, + cred->data_size); + cred_obj = json_object (); + json_object_set_new (cred_obj, "value", json_string (tmp_value)); + json_object_set_new (cred_obj, "name", json_string (cred->name)); + type = GNUNET_RECLAIM_credential_number_to_typename (cred->type); + json_object_set_new (cred_obj, "type", json_string (type)); if (NULL != issuer) { - json_object_set_new (attest_obj, "issuer", json_string (issuer)); + json_object_set_new (cred_obj, "issuer", json_string (issuer)); GNUNET_free (issuer); } - if (GNUNET_OK == GNUNET_RECLAIM_attestation_get_expiration (attest, + if (GNUNET_OK == GNUNET_RECLAIM_credential_get_expiration (cred, &exp)) { - json_object_set_new (attest_obj, "expiration", json_integer ( + json_object_set_new (cred_obj, "expiration", json_integer ( exp.abs_value_us)); } - id_str = GNUNET_STRINGS_data_to_string_alloc (&attest->id, - sizeof(attest->id)); - json_object_set_new (attest_obj, "id", json_string (id_str)); + id_str = GNUNET_STRINGS_data_to_string_alloc (&cred->id, + sizeof(cred->id)); + json_object_set_new (cred_obj, "id", json_string (id_str)); GNUNET_free (tmp_value); GNUNET_free (id_str); if (NULL != attrs) @@ -606,27 +606,27 @@ attest_collect (void *cls, type = GNUNET_RECLAIM_attribute_number_to_typename (ale->attribute->type); json_object_set_new (attr_obj, "type", json_string (type)); json_object_set_new (attr_obj, "id", json_string ("")); - json_object_set_new (attr_obj, "attestation", json_string ("")); + json_object_set_new (attr_obj, "credential", json_string ("")); json_array_append_new (attr_arr, attr_obj); GNUNET_free (tmp_value); } - json_object_set_new (attest_obj, "attributes", attr_arr); + json_object_set_new (cred_obj, "attributes", attr_arr); } - json_array_append_new (handle->resp_object, attest_obj); + json_array_append_new (handle->resp_object, cred_obj); GNUNET_RECLAIM_attribute_list_destroy (attrs); - GNUNET_RECLAIM_get_attestations_next (handle->attest_it); + GNUNET_RECLAIM_get_credentials_next (handle->cred_it); } /** - * Lists attestation for identity request + * Lists credential for identity request * * @param con_handle the connection handle * @param url the url * @param cls the RequestHandle */ static void -list_attestation_cont (struct GNUNET_REST_RequestHandle *con_handle, +list_credential_cont (struct GNUNET_REST_RequestHandle *con_handle, const char *url, void *cls) { @@ -636,9 +636,9 @@ list_attestation_cont (struct GNUNET_REST_RequestHandle *con_handle, char *identity; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Getting attestations for %s.\n", + "Getting credentials for %s.\n", handle->url); - if (strlen (GNUNET_REST_API_NS_RECLAIM_ATTESTATION) >= strlen ( + if (strlen (GNUNET_REST_API_NS_RECLAIM_CREDENTIAL) >= strlen ( handle->url)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n"); @@ -646,7 +646,7 @@ list_attestation_cont (struct GNUNET_REST_RequestHandle *con_handle, return; } identity = handle->url + strlen ( - GNUNET_REST_API_NS_RECLAIM_ATTESTATION) + 1; + GNUNET_REST_API_NS_RECLAIM_CREDENTIAL) + 1; for (ego_entry = ego_head; NULL != ego_entry; ego_entry = ego_entry->next) @@ -663,11 +663,11 @@ list_attestation_cont (struct GNUNET_REST_RequestHandle *con_handle, return; } priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego); - handle->attest_it = GNUNET_RECLAIM_get_attestations_start (idp, + handle->cred_it = GNUNET_RECLAIM_get_credentials_start (idp, priv_key, &collect_error_cb, handle, - &attest_collect, + &cred_collect, handle, & collect_finished_cb, @@ -676,27 +676,27 @@ list_attestation_cont (struct GNUNET_REST_RequestHandle *con_handle, /** - * Deletes attestation from an identity + * Deletes credential from an identity * * @param con_handle the connection handle * @param url the url * @param cls the RequestHandle */ static void -delete_attestation_cont (struct GNUNET_REST_RequestHandle *con_handle, +delete_credential_cont (struct GNUNET_REST_RequestHandle *con_handle, const char *url, void *cls) { struct RequestHandle *handle = cls; const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key; - struct GNUNET_RECLAIM_Attestation attr; + struct GNUNET_RECLAIM_Credential attr; struct EgoEntry *ego_entry; char *identity_id_str; char *identity; char *id; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Deleting attestation.\n"); - if (strlen (GNUNET_REST_API_NS_RECLAIM_ATTESTATION) >= strlen ( + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Deleting credential.\n"); + if (strlen (GNUNET_REST_API_NS_RECLAIM_CREDENTIAL) >= strlen ( handle->url)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n"); @@ -705,7 +705,7 @@ delete_attestation_cont (struct GNUNET_REST_RequestHandle *con_handle, } identity_id_str = strdup (handle->url + strlen ( - GNUNET_REST_API_NS_RECLAIM_ATTESTATION) + 1); + GNUNET_REST_API_NS_RECLAIM_CREDENTIAL) + 1); identity = strtok (identity_id_str, "/"); id = strtok (NULL, "/"); if ((NULL == identity) || (NULL == id)) @@ -730,10 +730,10 @@ delete_attestation_cont (struct GNUNET_REST_RequestHandle *con_handle, return; } priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego); - memset (&attr, 0, sizeof(struct GNUNET_RECLAIM_Attestation)); + memset (&attr, 0, sizeof(struct GNUNET_RECLAIM_Credential)); GNUNET_STRINGS_string_to_data (id, strlen (id), &attr.id, sizeof(attr.id)); attr.name = ""; - handle->idp_op = GNUNET_RECLAIM_attestation_delete (idp, + handle->idp_op = GNUNET_RECLAIM_credential_delete (idp, priv_key, &attr, &delete_finished_cb, @@ -877,13 +877,13 @@ add_attribute_cont (struct GNUNET_REST_RequestHandle *con_handle, /** * Parse a JWT and return the respective claim value as Attribute * - * @param attest the jwt attestation + * @param cred the jwt credential * @param claim the name of the claim in the JWT * * @return a GNUNET_RECLAIM_Attribute, containing the new value */ struct GNUNET_RECLAIM_Attribute * -parse_jwt (const struct GNUNET_RECLAIM_Attestation *attest, +parse_jwt (const struct GNUNET_RECLAIM_Credential *cred, const char *claim) { char *jwt_string; @@ -899,9 +899,9 @@ parse_jwt (const struct GNUNET_RECLAIM_Attestation *attest, json_t *json_val; json_error_t *json_err = NULL; - jwt_string = GNUNET_RECLAIM_attestation_value_to_string (attest->type, - attest->data, - attest->data_size); + jwt_string = GNUNET_RECLAIM_credential_value_to_string (cred->type, + cred->data, + cred->data_size); char *jwt_body = strtok (jwt_string, delim); jwt_body = strtok (NULL, delim); GNUNET_STRINGS_base64_decode (jwt_body, strlen (jwt_body), @@ -927,16 +927,16 @@ parse_jwt (const struct GNUNET_RECLAIM_Attestation *attest, "Error: Referenced Claim Name not Found", (void **) &data, &data_size); - attr = GNUNET_RECLAIM_attribute_new (claim, &attest->id, + attr = GNUNET_RECLAIM_attribute_new (claim, &cred->id, type, data, data_size); - attr->id = attest->id; + attr->id = cred->id; attr->flag = 1; } else { - attr = GNUNET_RECLAIM_attribute_new (claim, &attest->id, + attr = GNUNET_RECLAIM_attribute_new (claim, &cred->id, type, data, data_size); - attr->id = attest->id; + attr->id = cred->id; attr->flag = 1; } return attr; @@ -965,7 +965,7 @@ attr_collect (void *cls, json_object_set_new (attr_obj, "value", json_string (tmp_value)); json_object_set_new (attr_obj, "name", json_string (attr->name)); - if (GNUNET_RECLAIM_id_is_zero (&attr->attestation)) + if (GNUNET_RECLAIM_id_is_zero (&attr->credential)) json_object_set_new (attr_obj, "flag", json_string ("0")); else json_object_set_new (attr_obj, "flag", json_string ("1")); @@ -974,9 +974,9 @@ attr_collect (void *cls, id_str = GNUNET_STRINGS_data_to_string_alloc (&attr->id, sizeof(attr->id)); json_object_set_new (attr_obj, "id", json_string (id_str)); - id_str = GNUNET_STRINGS_data_to_string_alloc (&attr->attestation, - sizeof(attr->attestation)); - json_object_set_new (attr_obj, "attestation", json_string (id_str)); + id_str = GNUNET_STRINGS_data_to_string_alloc (&attr->credential, + sizeof(attr->credential)); + json_object_set_new (attr_obj, "credential", json_string (id_str)); json_array_append (handle->resp_object, attr_obj); json_decref (attr_obj); GNUNET_free (tmp_value); @@ -1180,7 +1180,7 @@ static void consume_cont (void *cls, const struct GNUNET_CRYPTO_EcdsaPublicKey *identity, const struct GNUNET_RECLAIM_Attribute *attr, - const struct GNUNET_RECLAIM_Attestation *attest) + const struct GNUNET_RECLAIM_Credential *cred) { struct RequestHandle *handle = cls; char *val_str; @@ -1427,11 +1427,11 @@ rest_identity_process_request (struct GNUNET_REST_RequestHandle *rest_handle, { MHD_HTTP_METHOD_DELETE, GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES, &delete_attribute_cont }, { MHD_HTTP_METHOD_GET, - GNUNET_REST_API_NS_RECLAIM_ATTESTATION, &list_attestation_cont }, + GNUNET_REST_API_NS_RECLAIM_CREDENTIAL, &list_credential_cont }, { MHD_HTTP_METHOD_POST, - GNUNET_REST_API_NS_RECLAIM_ATTESTATION, &add_attestation_cont }, + GNUNET_REST_API_NS_RECLAIM_CREDENTIAL, &add_credential_cont }, { MHD_HTTP_METHOD_DELETE, - GNUNET_REST_API_NS_RECLAIM_ATTESTATION, &delete_attestation_cont }, + GNUNET_REST_API_NS_RECLAIM_CREDENTIAL, &delete_credential_cont }, { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY_TICKETS, &list_tickets_cont }, { MHD_HTTP_METHOD_POST, diff --git a/src/reclaim/reclaim.h b/src/reclaim/reclaim.h index 7b5d7ab19..6ba9fdcd7 100644 --- a/src/reclaim/reclaim.h +++ b/src/reclaim/reclaim.h @@ -139,9 +139,9 @@ struct AttributeResultMessage uint16_t attr_len GNUNET_PACKED; /** - * Length of serialized attestation data + * Length of serialized credential data */ - uint16_t attestation_len GNUNET_PACKED; + uint16_t credential_len GNUNET_PACKED; /** * always zero (for alignment) @@ -159,9 +159,9 @@ struct AttributeResultMessage }; /** - * Attestation is returned from the idp. + * Credential is returned from the idp. */ -struct AttestationResultMessage +struct CredentialResultMessage { /** * Message header @@ -176,7 +176,7 @@ struct AttestationResultMessage /** * Length of serialized attribute data */ - uint16_t attestation_len GNUNET_PACKED; + uint16_t credential_len GNUNET_PACKED; /** * always zero (for alignment) @@ -189,7 +189,7 @@ struct AttestationResultMessage struct GNUNET_CRYPTO_EcdsaPublicKey identity; /* followed by: - * serialized attestation data + * serialized credential data */ }; @@ -234,9 +234,9 @@ struct AttributeIterationNextMessage /** - * Start a attestation iteration for the given identity + * Start a credential iteration for the given identity */ -struct AttestationIterationStartMessage +struct CredentialIterationStartMessage { /** * Message @@ -256,9 +256,9 @@ struct AttestationIterationStartMessage /** - * Ask for next result of attestation iteration for the given operation + * Ask for next result of credential iteration for the given operation */ -struct AttestationIterationNextMessage +struct CredentialIterationNextMessage { /** * Type will be #GNUNET_MESSAGE_TYPE_RECLAIM_ATTRIBUTE_ITERATION_NEXT @@ -273,9 +273,9 @@ struct AttestationIterationNextMessage /** - * Stop attestation iteration for the given operation + * Stop credential iteration for the given operation */ -struct AttestationIterationStopMessage +struct CredentialIterationStopMessage { /** * Type will be #GNUNET_MESSAGE_TYPE_RECLAIM_ATTRIBUTE_ITERATION_STOP @@ -520,9 +520,9 @@ struct ConsumeTicketResultMessage uint16_t attrs_len GNUNET_PACKED; /** - * Length of attestation data + * Length of presentation data */ - uint16_t attestations_len; + uint16_t presentations_len; /** * always zero (for alignment) diff --git a/src/reclaim/reclaim_api.c b/src/reclaim/reclaim_api.c index b432b4f2a..2cfcbad09 100644 --- a/src/reclaim/reclaim_api.c +++ b/src/reclaim/reclaim_api.c @@ -77,9 +77,9 @@ struct GNUNET_RECLAIM_Operation GNUNET_RECLAIM_AttributeTicketResult atr_cb; /** - * Attestation result callback + * Credential result callback */ - GNUNET_RECLAIM_AttestationResult at_cb; + GNUNET_RECLAIM_CredentialResult at_cb; /** * Revocation result callback @@ -239,19 +239,19 @@ struct GNUNET_RECLAIM_AttributeIterator }; /** - * Handle for a attestation iterator operation + * Handle for a credential iterator operation */ -struct GNUNET_RECLAIM_AttestationIterator +struct GNUNET_RECLAIM_CredentialIterator { /** * Kept in a DLL. */ - struct GNUNET_RECLAIM_AttestationIterator *next; + struct GNUNET_RECLAIM_CredentialIterator *next; /** * Kept in a DLL. */ - struct GNUNET_RECLAIM_AttestationIterator *prev; + struct GNUNET_RECLAIM_CredentialIterator *prev; /** * Main handle to access the service. @@ -271,7 +271,7 @@ struct GNUNET_RECLAIM_AttestationIterator /** * The continuation to call with the results */ - GNUNET_RECLAIM_AttestationResult proc; + GNUNET_RECLAIM_CredentialResult proc; /** * Closure for @e proc. @@ -349,12 +349,12 @@ struct GNUNET_RECLAIM_Handle /** * Head of active iterations */ - struct GNUNET_RECLAIM_AttestationIterator *ait_head; + struct GNUNET_RECLAIM_CredentialIterator *ait_head; /** * Tail of active iterations */ - struct GNUNET_RECLAIM_AttestationIterator *ait_tail; + struct GNUNET_RECLAIM_CredentialIterator *ait_tail; /** * Head of active iterations @@ -464,7 +464,7 @@ free_it (struct GNUNET_RECLAIM_AttributeIterator *it) * @param ait entry to free */ static void -free_ait (struct GNUNET_RECLAIM_AttestationIterator *ait) +free_ait (struct GNUNET_RECLAIM_CredentialIterator *ait) { struct GNUNET_RECLAIM_Handle *h = ait->h; @@ -561,13 +561,13 @@ check_consume_ticket_result (void *cls, { size_t msg_len; size_t attrs_len; - size_t attests_len; + size_t pl_len; msg_len = ntohs (msg->header.size); attrs_len = ntohs (msg->attrs_len); - attests_len = ntohs (msg->attestations_len); + pl_len = ntohs (msg->presentations_len); if (msg_len != - sizeof(struct ConsumeTicketResultMessage) + attrs_len + attests_len) + sizeof(struct ConsumeTicketResultMessage) + attrs_len + pl_len) { GNUNET_break (0); return GNUNET_SYSERR; @@ -590,12 +590,12 @@ handle_consume_ticket_result (void *cls, struct GNUNET_RECLAIM_Handle *h = cls; struct GNUNET_RECLAIM_Operation *op; size_t attrs_len; - size_t attests_len; + size_t pl_len; uint32_t r_id = ntohl (msg->id); char *read_ptr; attrs_len = ntohs (msg->attrs_len); - attests_len = ntohs (msg->attestations_len); + pl_len = ntohs (msg->presentations_len); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Processing ticket result.\n"); @@ -608,13 +608,12 @@ handle_consume_ticket_result (void *cls, { struct GNUNET_RECLAIM_AttributeList *attrs; struct GNUNET_RECLAIM_AttributeListEntry *le; - struct GNUNET_RECLAIM_AttestationList *attests; - struct GNUNET_RECLAIM_AttestationListEntry *ale; + struct GNUNET_RECLAIM_PresentationList *pl; + struct GNUNET_RECLAIM_PresentationListEntry *ple; attrs = GNUNET_RECLAIM_attribute_list_deserialize ((char *) &msg[1], attrs_len); read_ptr = ((char *) &msg[1]) + attrs_len; - attests = - GNUNET_RECLAIM_attestation_list_deserialize (read_ptr, attests_len); + pl = GNUNET_RECLAIM_presentation_list_deserialize (read_ptr, pl_len); if (NULL != op->atr_cb) { if (NULL == attrs) @@ -626,22 +625,22 @@ handle_consume_ticket_result (void *cls, for (le = attrs->list_head; NULL != le; le = le->next) { if (GNUNET_NO == - GNUNET_RECLAIM_id_is_zero (&le->attribute->attestation)) + GNUNET_RECLAIM_id_is_zero (&le->attribute->credential)) { - for (ale = attests->list_head; NULL != ale; ale = ale->next) + for (ple = pl->list_head; NULL != ple; ple = ple->next) { if (GNUNET_YES == - GNUNET_RECLAIM_id_is_equal (&le->attribute->attestation, - &ale->attestation->id)) + GNUNET_RECLAIM_id_is_equal (&le->attribute->credential, + &ple->presentation->credential_id)) { op->atr_cb (op->cls, &msg->identity, - le->attribute, ale->attestation); + le->attribute, ple->presentation); break; } } } - else // No attestations + else // No credentials { op->atr_cb (op->cls, &msg->identity, le->attribute, NULL); @@ -649,10 +648,10 @@ handle_consume_ticket_result (void *cls, } if (NULL != attrs) GNUNET_RECLAIM_attribute_list_destroy (attrs); - if (NULL != attests) - GNUNET_RECLAIM_attestation_list_destroy (attests); + if (NULL != pl) + GNUNET_RECLAIM_presentation_list_destroy (pl); attrs = NULL; - attests = NULL; + pl = NULL; } op->atr_cb (op->cls, NULL, NULL, NULL); } @@ -768,21 +767,21 @@ handle_attribute_result (void *cls, const struct AttributeResultMessage *msg) /** * Handle an incoming message of type - * #GNUNET_MESSAGE_TYPE_RECLAIM_attestation_RESULT + * #GNUNET_MESSAGE_TYPE_RECLAIM_credential_RESULT * * @param cls * @param msg the message we received * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ static int -check_attestation_result (void *cls, const struct AttestationResultMessage *msg) +check_credential_result (void *cls, const struct CredentialResultMessage *msg) { size_t msg_len; - size_t attest_len; + size_t cred_len; msg_len = ntohs (msg->header.size); - attest_len = ntohs (msg->attestation_len); - if (msg_len != sizeof(struct AttestationResultMessage) + attest_len) + cred_len = ntohs (msg->credential_len); + if (msg_len != sizeof(struct CredentialResultMessage) + cred_len) { GNUNET_break (0); return GNUNET_SYSERR; @@ -793,24 +792,24 @@ check_attestation_result (void *cls, const struct AttestationResultMessage *msg) /** * Handle an incoming message of type - * #GNUNET_MESSAGE_TYPE_RECLAIM_attestation_RESULT + * #GNUNET_MESSAGE_TYPE_RECLAIM_credential_RESULT * * @param cls * @param msg the message we received */ static void -handle_attestation_result (void *cls, const struct - AttestationResultMessage *msg) +handle_credential_result (void *cls, const struct + CredentialResultMessage *msg) { static struct GNUNET_CRYPTO_EcdsaPrivateKey identity_dummy; struct GNUNET_RECLAIM_Handle *h = cls; - struct GNUNET_RECLAIM_AttestationIterator *it; + struct GNUNET_RECLAIM_CredentialIterator *it; struct GNUNET_RECLAIM_Operation *op; size_t att_len; uint32_t r_id = ntohl (msg->id); - att_len = ntohs (msg->attestation_len); - LOG (GNUNET_ERROR_TYPE_DEBUG, "Processing attestation result.\n"); + att_len = ntohs (msg->credential_len); + LOG (GNUNET_ERROR_TYPE_DEBUG, "Processing credential result.\n"); for (it = h->ait_head; NULL != it; it = it->next) @@ -848,8 +847,8 @@ handle_attestation_result (void *cls, const struct } { - struct GNUNET_RECLAIM_Attestation *att; - att = GNUNET_RECLAIM_attestation_deserialize ((char *) &msg[1], att_len); + struct GNUNET_RECLAIM_Credential *att; + att = GNUNET_RECLAIM_credential_deserialize ((char *) &msg[1], att_len); if (NULL != it) { @@ -986,9 +985,9 @@ reconnect (struct GNUNET_RECLAIM_Handle *h) GNUNET_MESSAGE_TYPE_RECLAIM_ATTRIBUTE_RESULT, struct AttributeResultMessage, h), - GNUNET_MQ_hd_var_size (attestation_result, - GNUNET_MESSAGE_TYPE_RECLAIM_ATTESTATION_RESULT, - struct AttestationResultMessage, + GNUNET_MQ_hd_var_size (credential_result, + GNUNET_MESSAGE_TYPE_RECLAIM_CREDENTIAL_RESULT, + struct CredentialResultMessage, h), GNUNET_MQ_hd_fixed_size (ticket_result, GNUNET_MESSAGE_TYPE_RECLAIM_TICKET_RESULT, @@ -1175,22 +1174,22 @@ GNUNET_RECLAIM_attribute_delete ( /** - * Store an attestation. If the attestation is already present, - * it is replaced with the new attestation. + * Store an credential. If the credential is already present, + * it is replaced with the new credential. * * @param h handle to the re:claimID service * @param pkey private key of the identity - * @param attr the attestation value - * @param exp_interval the relative expiration interval for the attestation + * @param attr the credential value + * @param exp_interval the relative expiration interval for the credential * @param cont continuation to call when done * @param cont_cls closure for @a cont * @return handle to abort the request */ struct GNUNET_RECLAIM_Operation * -GNUNET_RECLAIM_attestation_store ( +GNUNET_RECLAIM_credential_store ( struct GNUNET_RECLAIM_Handle *h, const struct GNUNET_CRYPTO_EcdsaPrivateKey *pkey, - const struct GNUNET_RECLAIM_Attestation *attr, + const struct GNUNET_RECLAIM_Credential *attr, const struct GNUNET_TIME_Relative *exp_interval, GNUNET_RECLAIM_ContinuationWithStatus cont, void *cont_cls) @@ -1205,15 +1204,15 @@ GNUNET_RECLAIM_attestation_store ( op->cls = cont_cls; op->r_id = h->r_id_gen++; GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, op); - attr_len = GNUNET_RECLAIM_attestation_serialize_get_size (attr); + attr_len = GNUNET_RECLAIM_credential_serialize_get_size (attr); op->env = GNUNET_MQ_msg_extra (sam, attr_len, - GNUNET_MESSAGE_TYPE_RECLAIM_ATTESTATION_STORE); + GNUNET_MESSAGE_TYPE_RECLAIM_CREDENTIAL_STORE); sam->identity = *pkey; sam->id = htonl (op->r_id); sam->exp = GNUNET_htonll (exp_interval->rel_value_us); - GNUNET_RECLAIM_attestation_serialize (attr, (char *) &sam[1]); + GNUNET_RECLAIM_credential_serialize (attr, (char *) &sam[1]); sam->attr_len = htons (attr_len); if (NULL != h->mq) @@ -1223,21 +1222,21 @@ GNUNET_RECLAIM_attestation_store ( /** - * Delete an attestation. Tickets used to share this attestation are updated + * Delete an credential. Tickets used to share this credential are updated * accordingly. * * @param h handle to the re:claimID service * @param pkey Private key of the identity to add an attribute to - * @param attr The attestation + * @param attr The credential * @param cont Continuation to call when done * @param cont_cls Closure for @a cont * @return handle Used to to abort the request */ struct GNUNET_RECLAIM_Operation * -GNUNET_RECLAIM_attestation_delete ( +GNUNET_RECLAIM_credential_delete ( struct GNUNET_RECLAIM_Handle *h, const struct GNUNET_CRYPTO_EcdsaPrivateKey *pkey, - const struct GNUNET_RECLAIM_Attestation *attr, + const struct GNUNET_RECLAIM_Credential *attr, GNUNET_RECLAIM_ContinuationWithStatus cont, void *cont_cls) { @@ -1251,13 +1250,13 @@ GNUNET_RECLAIM_attestation_delete ( op->cls = cont_cls; op->r_id = h->r_id_gen++; GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, op); - attr_len = GNUNET_RECLAIM_attestation_serialize_get_size (attr); + attr_len = GNUNET_RECLAIM_credential_serialize_get_size (attr); op->env = GNUNET_MQ_msg_extra (dam, attr_len, - GNUNET_MESSAGE_TYPE_RECLAIM_ATTESTATION_DELETE); + GNUNET_MESSAGE_TYPE_RECLAIM_CREDENTIAL_DELETE); dam->identity = *pkey; dam->id = htonl (op->r_id); - GNUNET_RECLAIM_attestation_serialize (attr, (char *) &dam[1]); + GNUNET_RECLAIM_credential_serialize (attr, (char *) &dam[1]); dam->attr_len = htons (attr_len); if (NULL != h->mq) @@ -1376,12 +1375,12 @@ GNUNET_RECLAIM_get_attributes_stop (struct GNUNET_RECLAIM_AttributeIterator *it) /** - * List all attestations for a local identity. + * List all credentials for a local identity. * This MUST lock the `struct GNUNET_RECLAIM_Handle` - * for any other calls than #GNUNET_RECLAIM_get_attestations_next() and - * #GNUNET_RECLAIM_get_attestations_stop. @a proc will be called once + * for any other calls than #GNUNET_RECLAIM_get_credentials_next() and + * #GNUNET_RECLAIM_get_credentials_stop. @a proc will be called once * immediately, and then again after - * #GNUNET_RECLAIM_get_attestations_next() is invoked. + * #GNUNET_RECLAIM_get_credentials_next() is invoked. * * On error (disconnect), @a error_cb will be invoked. * On normal completion, @a finish_cb proc will be @@ -1392,31 +1391,31 @@ GNUNET_RECLAIM_get_attributes_stop (struct GNUNET_RECLAIM_AttributeIterator *it) * @param error_cb Function to call on error (i.e. disconnect), * the handle is afterwards invalid * @param error_cb_cls Closure for @a error_cb - * @param proc Function to call on each attestation + * @param proc Function to call on each credential * @param proc_cls Closure for @a proc * @param finish_cb Function to call on completion * the handle is afterwards invalid * @param finish_cb_cls Closure for @a finish_cb * @return an iterator Handle to use for iteration */ -struct GNUNET_RECLAIM_AttestationIterator * -GNUNET_RECLAIM_get_attestations_start ( +struct GNUNET_RECLAIM_CredentialIterator * +GNUNET_RECLAIM_get_credentials_start ( struct GNUNET_RECLAIM_Handle *h, const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity, GNUNET_SCHEDULER_TaskCallback error_cb, void *error_cb_cls, - GNUNET_RECLAIM_AttestationResult proc, + GNUNET_RECLAIM_CredentialResult proc, void *proc_cls, GNUNET_SCHEDULER_TaskCallback finish_cb, void *finish_cb_cls) { - struct GNUNET_RECLAIM_AttestationIterator *ait; + struct GNUNET_RECLAIM_CredentialIterator *ait; struct GNUNET_MQ_Envelope *env; - struct AttestationIterationStartMessage *msg; + struct CredentialIterationStartMessage *msg; uint32_t rid; rid = h->r_id_gen++; - ait = GNUNET_new (struct GNUNET_RECLAIM_AttestationIterator); + ait = GNUNET_new (struct GNUNET_RECLAIM_CredentialIterator); ait->h = h; ait->error_cb = error_cb; ait->error_cb_cls = error_cb_cls; @@ -1429,7 +1428,7 @@ GNUNET_RECLAIM_get_attestations_start ( GNUNET_CONTAINER_DLL_insert_tail (h->ait_head, h->ait_tail, ait); env = GNUNET_MQ_msg (msg, - GNUNET_MESSAGE_TYPE_RECLAIM_ATTESTATION_ITERATION_START); + GNUNET_MESSAGE_TYPE_RECLAIM_CREDENTIAL_ITERATION_START); msg->id = htonl (rid); msg->identity = *identity; if (NULL == h->mq) @@ -1441,21 +1440,21 @@ GNUNET_RECLAIM_get_attestations_start ( /** - * Calls the record processor specified in #GNUNET_RECLAIM_get_attestation_start + * Calls the record processor specified in #GNUNET_RECLAIM_get_credential_start * for the next record. * * @param it the iterator */ void -GNUNET_RECLAIM_get_attestations_next (struct - GNUNET_RECLAIM_AttestationIterator *ait) +GNUNET_RECLAIM_get_credentials_next (struct + GNUNET_RECLAIM_CredentialIterator *ait) { struct GNUNET_RECLAIM_Handle *h = ait->h; - struct AttestationIterationNextMessage *msg; + struct CredentialIterationNextMessage *msg; struct GNUNET_MQ_Envelope *env; env = - GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_RECLAIM_ATTESTATION_ITERATION_NEXT); + GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_RECLAIM_CREDENTIAL_ITERATION_NEXT); msg->id = htonl (ait->r_id); GNUNET_MQ_send (h->mq, env); } @@ -1469,18 +1468,18 @@ GNUNET_RECLAIM_get_attestations_next (struct * @param it the iterator */ void -GNUNET_RECLAIM_get_attestations_stop (struct - GNUNET_RECLAIM_AttestationIterator *ait) +GNUNET_RECLAIM_get_credentials_stop (struct + GNUNET_RECLAIM_CredentialIterator *ait) { struct GNUNET_RECLAIM_Handle *h = ait->h; struct GNUNET_MQ_Envelope *env; - struct AttestationIterationStopMessage *msg; + struct CredentialIterationStopMessage *msg; if (NULL != h->mq) { env = GNUNET_MQ_msg (msg, - GNUNET_MESSAGE_TYPE_RECLAIM_ATTESTATION_ITERATION_STOP); + GNUNET_MESSAGE_TYPE_RECLAIM_CREDENTIAL_ITERATION_STOP); msg->id = htonl (ait->r_id); GNUNET_MQ_send (h->mq, env); } diff --git a/src/reclaim/reclaim_attestation.c b/src/reclaim/reclaim_attestation.c deleted file mode 100644 index 1f2b1890b..000000000 --- a/src/reclaim/reclaim_attestation.c +++ /dev/null @@ -1,570 +0,0 @@ -/* - This file is part of GNUnet - Copyright (C) 2010-2015 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - 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 - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file reclaim-attribute/reclaim_attestation.c - * @brief helper library to manage identity attribute attestations - * @author Martin Schanzenbach - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_reclaim_plugin.h" -#include "reclaim_attestation.h" - - -/** - * Handle for a plugin - */ -struct Plugin -{ - /** - * Name of the plugin - */ - char *library_name; - - /** - * Plugin API - */ - struct GNUNET_RECLAIM_AttestationPluginFunctions *api; -}; - - -/** - * Plugins - */ -static struct Plugin **attest_plugins; - - -/** - * Number of plugins - */ -static unsigned int num_plugins; - - -/** - * Init canary - */ -static int initialized; - - -/** - * Add a plugin - * - * @param cls closure - * @param library_name name of the API library - * @param lib_ret the plugin API pointer - */ -static void -add_plugin (void *cls, const char *library_name, void *lib_ret) -{ - struct GNUNET_RECLAIM_AttestationPluginFunctions *api = lib_ret; - struct Plugin *plugin; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Loading attestation plugin `%s'\n", - library_name); - plugin = GNUNET_new (struct Plugin); - plugin->api = api; - plugin->library_name = GNUNET_strdup (library_name); - GNUNET_array_append (attest_plugins, num_plugins, plugin); -} - - -/** - * Load plugins - */ -static void -init () -{ - if (GNUNET_YES == initialized) - return; - initialized = GNUNET_YES; - GNUNET_PLUGIN_load_all ("libgnunet_plugin_reclaim_attestation_", - NULL, - &add_plugin, - NULL); -} - - -/** - * Convert an attestation type name to the corresponding number - * - * @param typename name to convert - * @return corresponding number, UINT32_MAX on error - */ -uint32_t -GNUNET_RECLAIM_attestation_typename_to_number (const char *typename) -{ - unsigned int i; - struct Plugin *plugin; - uint32_t ret; - init (); - for (i = 0; i < num_plugins; i++) - { - plugin = attest_plugins[i]; - if (UINT32_MAX != - (ret = plugin->api->typename_to_number (plugin->api->cls, - typename))) - return ret; - } - return UINT32_MAX; -} - - -/** - * Convert an attestation type number to the corresponding attestation type string - * - * @param type number of a type - * @return corresponding typestring, NULL on error - */ -const char * -GNUNET_RECLAIM_attestation_number_to_typename (uint32_t type) -{ - unsigned int i; - struct Plugin *plugin; - const char *ret; - - init (); - for (i = 0; i < num_plugins; i++) - { - plugin = attest_plugins[i]; - if (NULL != - (ret = plugin->api->number_to_typename (plugin->api->cls, type))) - return ret; - } - return NULL; -} - - -/** - * Convert human-readable version of a 'claim' of an attestation to the binary - * representation - * - * @param type type of the claim - * @param s human-readable string - * @param data set to value in binary encoding (will be allocated) - * @param data_size set to number of bytes in @a data - * @return #GNUNET_OK on success - */ -int -GNUNET_RECLAIM_attestation_string_to_value (uint32_t type, - const char *s, - void **data, - size_t *data_size) -{ - unsigned int i; - struct Plugin *plugin; - - init (); - for (i = 0; i < num_plugins; i++) - { - plugin = attest_plugins[i]; - if (GNUNET_OK == plugin->api->string_to_value (plugin->api->cls, - type, - s, - data, - data_size)) - return GNUNET_OK; - } - return GNUNET_SYSERR; -} - - -/** - * Convert the 'claim' of an attestation to a string - * - * @param type the type of attestation - * @param data claim in binary encoding - * @param data_size number of bytes in @a data - * @return NULL on error, otherwise human-readable representation of the claim - */ -char * -GNUNET_RECLAIM_attestation_value_to_string (uint32_t type, - const void *data, - size_t data_size) -{ - unsigned int i; - struct Plugin *plugin; - char *ret; - - init (); - for (i = 0; i < num_plugins; i++) - { - plugin = attest_plugins[i]; - if (NULL != (ret = plugin->api->value_to_string (plugin->api->cls, - type, - data, - data_size))) - return ret; - } - return NULL; -} - - -/** - * Create a new attestation. - * - * @param attr_name the attestation name - * @param type the attestation type - * @param data the attestation value - * @param data_size the attestation value size - * @return the new attestation - */ -struct GNUNET_RECLAIM_Attestation * -GNUNET_RECLAIM_attestation_new (const char *attr_name, - uint32_t type, - const void *data, - size_t data_size) -{ - struct GNUNET_RECLAIM_Attestation *attr; - char *write_ptr; - char *attr_name_tmp = GNUNET_strdup (attr_name); - - GNUNET_STRINGS_utf8_tolower (attr_name, attr_name_tmp); - - attr = GNUNET_malloc (sizeof(struct GNUNET_RECLAIM_Attestation) - + strlen (attr_name_tmp) + 1 + data_size); - attr->type = type; - attr->data_size = data_size; - attr->flag = 0; - write_ptr = (char *) &attr[1]; - GNUNET_memcpy (write_ptr, attr_name_tmp, strlen (attr_name_tmp) + 1); - attr->name = write_ptr; - write_ptr += strlen (attr->name) + 1; - GNUNET_memcpy (write_ptr, data, data_size); - attr->data = write_ptr; - GNUNET_free (attr_name_tmp); - return attr; -} - - -/** - * Get required size for serialization buffer - * - * @param attrs the attribute list to serialize - * @return the required buffer size - */ -size_t -GNUNET_RECLAIM_attestation_list_serialize_get_size ( - const struct GNUNET_RECLAIM_AttestationList *attestations) -{ - struct GNUNET_RECLAIM_AttestationListEntry *le; - size_t len = 0; - - for (le = attestations->list_head; NULL != le; le = le->next) - { - GNUNET_assert (NULL != le->attestation); - len += GNUNET_RECLAIM_attestation_serialize_get_size (le->attestation); - len += sizeof(struct GNUNET_RECLAIM_AttestationListEntry); - } - return len; -} - - -/** - * Serialize an attribute list - * - * @param attrs the attribute list to serialize - * @param result the serialized attribute - * @return length of serialized data - */ -size_t -GNUNET_RECLAIM_attestation_list_serialize ( - const struct GNUNET_RECLAIM_AttestationList *attestations, - char *result) -{ - struct GNUNET_RECLAIM_AttestationListEntry *le; - size_t len; - size_t total_len; - char *write_ptr; - write_ptr = result; - total_len = 0; - for (le = attestations->list_head; NULL != le; le = le->next) - { - GNUNET_assert (NULL != le->attestation); - len = GNUNET_RECLAIM_attestation_serialize (le->attestation, write_ptr); - total_len += len; - write_ptr += len; - } - return total_len; -} - - -/** - * Deserialize an attestation list - * - * @param data the serialized attribute list - * @param data_size the length of the serialized data - * @return a GNUNET_IDENTITY_PROVIDER_AttributeList, must be free'd by caller - */ -struct GNUNET_RECLAIM_AttestationList * -GNUNET_RECLAIM_attestation_list_deserialize (const char *data, size_t data_size) -{ - struct GNUNET_RECLAIM_AttestationList *al; - struct GNUNET_RECLAIM_AttestationListEntry *ale; - size_t att_len; - const char *read_ptr; - - al = GNUNET_new (struct GNUNET_RECLAIM_AttestationList); - - if ((data_size < sizeof(struct - Attestation) - + sizeof(struct GNUNET_RECLAIM_AttestationListEntry))) - return al; - - read_ptr = data; - while (((data + data_size) - read_ptr) >= sizeof(struct Attestation)) - { - ale = GNUNET_new (struct GNUNET_RECLAIM_AttestationListEntry); - ale->attestation = - GNUNET_RECLAIM_attestation_deserialize (read_ptr, - data_size - (read_ptr - data)); - if (NULL == ale->attestation) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to deserialize malformed attestation.\n"); - GNUNET_free (ale); - return al; - } - GNUNET_CONTAINER_DLL_insert (al->list_head, al->list_tail, ale); - att_len = GNUNET_RECLAIM_attestation_serialize_get_size (ale->attestation); - read_ptr += att_len; - } - return al; -} - - -/** - * Make a (deep) copy of the attestation list - * @param attrs claim list to copy - * @return copied claim list - */ -struct GNUNET_RECLAIM_AttestationList * -GNUNET_RECLAIM_attestation_list_dup ( - const struct GNUNET_RECLAIM_AttestationList *al) -{ - struct GNUNET_RECLAIM_AttestationListEntry *ale; - struct GNUNET_RECLAIM_AttestationListEntry *result_ale; - struct GNUNET_RECLAIM_AttestationList *result; - - result = GNUNET_new (struct GNUNET_RECLAIM_AttestationList); - for (ale = al->list_head; NULL != ale; ale = ale->next) - { - result_ale = GNUNET_new (struct GNUNET_RECLAIM_AttestationListEntry); - GNUNET_assert (NULL != ale->attestation); - result_ale->attestation = - GNUNET_RECLAIM_attestation_new (ale->attestation->name, - ale->attestation->type, - ale->attestation->data, - ale->attestation->data_size); - result_ale->attestation->id = ale->attestation->id; - GNUNET_CONTAINER_DLL_insert (result->list_head, - result->list_tail, - result_ale); - } - return result; -} - - -/** - * Destroy attestation list - * - * @param attrs list to destroy - */ -void -GNUNET_RECLAIM_attestation_list_destroy ( - struct GNUNET_RECLAIM_AttestationList *al) -{ - struct GNUNET_RECLAIM_AttestationListEntry *ale; - struct GNUNET_RECLAIM_AttestationListEntry *tmp_ale; - - for (ale = al->list_head; NULL != ale;) - { - if (NULL != ale->attestation) - GNUNET_free (ale->attestation); - tmp_ale = ale; - ale = ale->next; - GNUNET_free (tmp_ale); - } - GNUNET_free (al); -} - - -/** - * Get required size for serialization buffer - * - * @param attr the attestation to serialize - * @return the required buffer size - */ -size_t -GNUNET_RECLAIM_attestation_serialize_get_size ( - const struct GNUNET_RECLAIM_Attestation *attestation) -{ - return sizeof(struct Attestation) + strlen (attestation->name) - + attestation->data_size; -} - - -/** - * Serialize an attestation - * - * @param attr the attestation to serialize - * @param result the serialized attestation - * @return length of serialized data - */ -size_t -GNUNET_RECLAIM_attestation_serialize ( - const struct GNUNET_RECLAIM_Attestation *attestation, - char *result) -{ - size_t data_len_ser; - size_t name_len; - struct Attestation *atts; - char *write_ptr; - - atts = (struct Attestation *) result; - atts->attestation_type = htons (attestation->type); - atts->attestation_flag = htonl (attestation->flag); - atts->attestation_id = attestation->id; - name_len = strlen (attestation->name); - atts->name_len = htons (name_len); - write_ptr = (char *) &atts[1]; - GNUNET_memcpy (write_ptr, attestation->name, name_len); - write_ptr += name_len; - // TODO plugin-ize - // data_len_ser = plugin->serialize_attribute_value (attr, - // &attr_ser[1]); - data_len_ser = attestation->data_size; - GNUNET_memcpy (write_ptr, attestation->data, attestation->data_size); - atts->data_size = htons (data_len_ser); - - return sizeof(struct Attestation) + strlen (attestation->name) - + attestation->data_size; -} - - -/** - * Deserialize an attestation - * - * @param data the serialized attestation - * @param data_size the length of the serialized data - * - * @return a GNUNET_IDENTITY_PROVIDER_Attribute, must be free'd by caller - */ -struct GNUNET_RECLAIM_Attestation * -GNUNET_RECLAIM_attestation_deserialize (const char *data, size_t data_size) -{ - struct GNUNET_RECLAIM_Attestation *attestation; - struct Attestation *atts; - size_t data_len; - size_t name_len; - char *write_ptr; - - if (data_size < sizeof(struct Attestation)) - return NULL; - - atts = (struct Attestation *) data; - data_len = ntohs (atts->data_size); - name_len = ntohs (atts->name_len); - if (data_size < sizeof(struct Attestation) + data_len + name_len) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Buffer too small to deserialize\n"); - return NULL; - } - attestation = GNUNET_malloc (sizeof(struct GNUNET_RECLAIM_Attestation) - + data_len + name_len + 1); - attestation->type = ntohs (atts->attestation_type); - attestation->flag = ntohl (atts->attestation_flag); - attestation->id = atts->attestation_id; - attestation->data_size = data_len; - - write_ptr = (char *) &attestation[1]; - GNUNET_memcpy (write_ptr, &atts[1], name_len); - write_ptr[name_len] = '\0'; - attestation->name = write_ptr; - - write_ptr += name_len + 1; - GNUNET_memcpy (write_ptr, (char *) &atts[1] + name_len, - attestation->data_size); - attestation->data = write_ptr; - return attestation; -} - - -struct GNUNET_RECLAIM_AttributeList* -GNUNET_RECLAIM_attestation_get_attributes (const struct - GNUNET_RECLAIM_Attestation *attest) -{ - unsigned int i; - struct Plugin *plugin; - struct GNUNET_RECLAIM_AttributeList *ret; - init (); - for (i = 0; i < num_plugins; i++) - { - plugin = attest_plugins[i]; - if (NULL != - (ret = plugin->api->get_attributes (plugin->api->cls, - attest))) - return ret; - } - return NULL; -} - - -char* -GNUNET_RECLAIM_attestation_get_issuer (const struct - GNUNET_RECLAIM_Attestation *attest) -{ - unsigned int i; - struct Plugin *plugin; - char *ret; - init (); - for (i = 0; i < num_plugins; i++) - { - plugin = attest_plugins[i]; - if (NULL != - (ret = plugin->api->get_issuer (plugin->api->cls, - attest))) - return ret; - } - return NULL; -} - - -int -GNUNET_RECLAIM_attestation_get_expiration (const struct - GNUNET_RECLAIM_Attestation *attest, - struct GNUNET_TIME_Absolute*exp) -{ - unsigned int i; - struct Plugin *plugin; - init (); - for (i = 0; i < num_plugins; i++) - { - plugin = attest_plugins[i]; - if (GNUNET_OK != plugin->api->get_expiration (plugin->api->cls, - attest, - exp)) - continue; - return GNUNET_OK; - } - return GNUNET_SYSERR; -} diff --git a/src/reclaim/reclaim_attestation.h b/src/reclaim/reclaim_attestation.h deleted file mode 100644 index 5747d8896..000000000 --- a/src/reclaim/reclaim_attestation.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2012-2015 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - 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 - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ -/** - * @author Martin Schanzenbach - * @file reclaim-attribute/reclaim_attestation.h - * @brief GNUnet reclaim identity attribute attestations - * - */ -#ifndef RECLAIM_ATTESTATION_H -#define RECLAIM_ATTESTATION_H - -#include "gnunet_reclaim_service.h" - -/** - * Serialized attestation claim - */ -struct Attestation -{ - /** - * Attestation type - */ - uint32_t attestation_type; - - /** - * Attestation flag - */ - uint32_t attestation_flag; - - /** - * Attestation ID - */ - struct GNUNET_RECLAIM_Identifier attestation_id; - - /** - * Name length - */ - uint32_t name_len; - - /** - * Data size - */ - uint32_t data_size; - - // followed by data_size Attestation value data -}; - -#endif diff --git a/src/reclaim/reclaim_attribute.c b/src/reclaim/reclaim_attribute.c index 971bfce23..2217987ac 100644 --- a/src/reclaim/reclaim_attribute.c +++ b/src/reclaim/reclaim_attribute.c @@ -222,7 +222,7 @@ GNUNET_RECLAIM_attribute_value_to_string (uint32_t type, * Create a new attribute. * * @param attr_name the attribute name - * @param attestation attestation ID of the attribute (maybe NULL) + * @param credential credential ID of the attribute (maybe NULL) * @param type the attribute type * @param data the attribute value * @param data_size the attribute value size @@ -231,7 +231,7 @@ GNUNET_RECLAIM_attribute_value_to_string (uint32_t type, struct GNUNET_RECLAIM_Attribute * GNUNET_RECLAIM_attribute_new (const char *attr_name, const struct - GNUNET_RECLAIM_Identifier *attestation, + GNUNET_RECLAIM_Identifier *credential, uint32_t type, const void *data, size_t data_size) @@ -244,8 +244,8 @@ GNUNET_RECLAIM_attribute_new (const char *attr_name, attr = GNUNET_malloc (sizeof(struct GNUNET_RECLAIM_Attribute) + strlen (attr_name_tmp) + 1 + data_size); - if (NULL != attestation) - attr->attestation = *attestation; + if (NULL != credential) + attr->credential = *credential; attr->type = type; attr->data_size = data_size; attr->flag = 0; @@ -272,7 +272,7 @@ void GNUNET_RECLAIM_attribute_list_add ( struct GNUNET_RECLAIM_AttributeList *al, const char *attr_name, - const struct GNUNET_RECLAIM_Identifier *attestation, + const struct GNUNET_RECLAIM_Identifier *credential, uint32_t type, const void *data, size_t data_size) @@ -281,7 +281,7 @@ GNUNET_RECLAIM_attribute_list_add ( ale = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry); ale->attribute = - GNUNET_RECLAIM_attribute_new (attr_name, attestation, + GNUNET_RECLAIM_attribute_new (attr_name, credential, type, data, data_size); GNUNET_CONTAINER_DLL_insert (al->list_head, al->list_tail, @@ -403,7 +403,7 @@ GNUNET_RECLAIM_attribute_list_dup ( { result_ale->attribute = GNUNET_RECLAIM_attribute_new (ale->attribute->name, - &ale->attribute->attestation, + &ale->attribute->credential, ale->attribute->type, ale->attribute->data, ale->attribute->data_size); @@ -478,7 +478,7 @@ GNUNET_RECLAIM_attribute_serialize ( attr_ser->attribute_type = htons (attr->type); attr_ser->attribute_flag = htonl (attr->flag); attr_ser->attribute_id = attr->id; - attr_ser->attestation_id = attr->attestation; + attr_ser->credential_id = attr->credential; name_len = strlen (attr->name); attr_ser->name_len = htons (name_len); write_ptr = (char *) &attr_ser[1]; @@ -530,7 +530,7 @@ GNUNET_RECLAIM_attribute_deserialize (const char *data, size_t data_size, attribute->type = ntohs (attr_ser->attribute_type); attribute->flag = ntohl (attr_ser->attribute_flag); attribute->id = attr_ser->attribute_id; - attribute->attestation = attr_ser->attestation_id; + attribute->credential = attr_ser->credential_id; attribute->data_size = data_len; write_ptr = (char *) &attribute[1]; diff --git a/src/reclaim/reclaim_attribute.h b/src/reclaim/reclaim_attribute.h index 203c88a34..285d75d83 100644 --- a/src/reclaim/reclaim_attribute.h +++ b/src/reclaim/reclaim_attribute.h @@ -51,9 +51,9 @@ struct Attribute struct GNUNET_RECLAIM_Identifier attribute_id; /** - * Attestation ID + * Credential ID */ - struct GNUNET_RECLAIM_Identifier attestation_id; + struct GNUNET_RECLAIM_Identifier credential_id; /** * Name length @@ -68,39 +68,6 @@ struct Attribute // followed by data_size Attribute value data }; -/** - * Serialized attestation claim - */ -struct Attestation -{ - /** - * Attestation type - */ - uint32_t attestation_type GNUNET_PACKED; - - /** - * Attestation flag - */ - uint32_t attestation_flag GNUNET_PACKED; - - /** - * Attestation ID - */ - struct GNUNET_RECLAIM_Identifier attestation_id; - - /** - * Name length - */ - uint32_t name_len GNUNET_PACKED; - - /** - * Data size - */ - uint32_t data_size GNUNET_PACKED; - - // followed by data_size Attestation value data -}; - GNUNET_NETWORK_STRUCT_BEGIN #endif diff --git a/src/reclaim/reclaim_credential.c b/src/reclaim/reclaim_credential.c new file mode 100644 index 000000000..10b17ae96 --- /dev/null +++ b/src/reclaim/reclaim_credential.c @@ -0,0 +1,570 @@ +/* + This file is part of GNUnet + Copyright (C) 2010-2015 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file reclaim/reclaim_credential.c + * @brief helper library to manage identity attribute credentials + * @author Martin Schanzenbach + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_reclaim_plugin.h" +#include "reclaim_credential.h" + + +/** + * Handle for a plugin + */ +struct Plugin +{ + /** + * Name of the plugin + */ + char *library_name; + + /** + * Plugin API + */ + struct GNUNET_RECLAIM_CredentialPluginFunctions *api; +}; + + +/** + * Plugins + */ +static struct Plugin **credential_plugins; + + +/** + * Number of plugins + */ +static unsigned int num_plugins; + + +/** + * Init canary + */ +static int initialized; + + +/** + * Add a plugin + * + * @param cls closure + * @param library_name name of the API library + * @param lib_ret the plugin API pointer + */ +static void +add_plugin (void *cls, const char *library_name, void *lib_ret) +{ + struct GNUNET_RECLAIM_CredentialPluginFunctions *api = lib_ret; + struct Plugin *plugin; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Loading credential plugin `%s'\n", + library_name); + plugin = GNUNET_new (struct Plugin); + plugin->api = api; + plugin->library_name = GNUNET_strdup (library_name); + GNUNET_array_append (credential_plugins, num_plugins, plugin); +} + + +/** + * Load plugins + */ +static void +init () +{ + if (GNUNET_YES == initialized) + return; + initialized = GNUNET_YES; + GNUNET_PLUGIN_load_all ("libgnunet_plugin_reclaim_credential_", + NULL, + &add_plugin, + NULL); +} + + +/** + * Convert an credential type name to the corresponding number + * + * @param typename name to convert + * @return corresponding number, UINT32_MAX on error + */ +uint32_t +GNUNET_RECLAIM_credential_typename_to_number (const char *typename) +{ + unsigned int i; + struct Plugin *plugin; + uint32_t ret; + init (); + for (i = 0; i < num_plugins; i++) + { + plugin = credential_plugins[i]; + if (UINT32_MAX != + (ret = plugin->api->typename_to_number (plugin->api->cls, + typename))) + return ret; + } + return UINT32_MAX; +} + + +/** + * Convert an credential type number to the corresponding credential type string + * + * @param type number of a type + * @return corresponding typestring, NULL on error + */ +const char * +GNUNET_RECLAIM_credential_number_to_typename (uint32_t type) +{ + unsigned int i; + struct Plugin *plugin; + const char *ret; + + init (); + for (i = 0; i < num_plugins; i++) + { + plugin = credential_plugins[i]; + if (NULL != + (ret = plugin->api->number_to_typename (plugin->api->cls, type))) + return ret; + } + return NULL; +} + + +/** + * Convert human-readable version of a 'claim' of an credential to the binary + * representation + * + * @param type type of the claim + * @param s human-readable string + * @param data set to value in binary encoding (will be allocated) + * @param data_size set to number of bytes in @a data + * @return #GNUNET_OK on success + */ +int +GNUNET_RECLAIM_credential_string_to_value (uint32_t type, + const char *s, + void **data, + size_t *data_size) +{ + unsigned int i; + struct Plugin *plugin; + + init (); + for (i = 0; i < num_plugins; i++) + { + plugin = credential_plugins[i]; + if (GNUNET_OK == plugin->api->string_to_value (plugin->api->cls, + type, + s, + data, + data_size)) + return GNUNET_OK; + } + return GNUNET_SYSERR; +} + + +/** + * Convert the 'claim' of an credential to a string + * + * @param type the type of credential + * @param data claim in binary encoding + * @param data_size number of bytes in @a data + * @return NULL on error, otherwise human-readable representation of the claim + */ +char * +GNUNET_RECLAIM_credential_value_to_string (uint32_t type, + const void *data, + size_t data_size) +{ + unsigned int i; + struct Plugin *plugin; + char *ret; + + init (); + for (i = 0; i < num_plugins; i++) + { + plugin = credential_plugins[i]; + if (NULL != (ret = plugin->api->value_to_string (plugin->api->cls, + type, + data, + data_size))) + return ret; + } + return NULL; +} + + +/** + * Create a new credential. + * + * @param attr_name the credential name + * @param type the credential type + * @param data the credential value + * @param data_size the credential value size + * @return the new credential + */ +struct GNUNET_RECLAIM_Credential * +GNUNET_RECLAIM_credential_new (const char *attr_name, + uint32_t type, + const void *data, + size_t data_size) +{ + struct GNUNET_RECLAIM_Credential *attr; + char *write_ptr; + char *attr_name_tmp = GNUNET_strdup (attr_name); + + GNUNET_STRINGS_utf8_tolower (attr_name, attr_name_tmp); + + attr = GNUNET_malloc (sizeof(struct GNUNET_RECLAIM_Credential) + + strlen (attr_name_tmp) + 1 + data_size); + attr->type = type; + attr->data_size = data_size; + attr->flag = 0; + write_ptr = (char *) &attr[1]; + GNUNET_memcpy (write_ptr, attr_name_tmp, strlen (attr_name_tmp) + 1); + attr->name = write_ptr; + write_ptr += strlen (attr->name) + 1; + GNUNET_memcpy (write_ptr, data, data_size); + attr->data = write_ptr; + GNUNET_free (attr_name_tmp); + return attr; +} + + +/** + * Get required size for serialization buffer + * + * @param attrs the attribute list to serialize + * @return the required buffer size + */ +size_t +GNUNET_RECLAIM_credential_list_serialize_get_size ( + const struct GNUNET_RECLAIM_CredentialList *credentials) +{ + struct GNUNET_RECLAIM_CredentialListEntry *le; + size_t len = 0; + + for (le = credentials->list_head; NULL != le; le = le->next) + { + GNUNET_assert (NULL != le->credential); + len += GNUNET_RECLAIM_credential_serialize_get_size (le->credential); + len += sizeof(struct GNUNET_RECLAIM_CredentialListEntry); + } + return len; +} + + +/** + * Serialize an attribute list + * + * @param attrs the attribute list to serialize + * @param result the serialized attribute + * @return length of serialized data + */ +size_t +GNUNET_RECLAIM_credential_list_serialize ( + const struct GNUNET_RECLAIM_CredentialList *credentials, + char *result) +{ + struct GNUNET_RECLAIM_CredentialListEntry *le; + size_t len; + size_t total_len; + char *write_ptr; + write_ptr = result; + total_len = 0; + for (le = credentials->list_head; NULL != le; le = le->next) + { + GNUNET_assert (NULL != le->credential); + len = GNUNET_RECLAIM_credential_serialize (le->credential, write_ptr); + total_len += len; + write_ptr += len; + } + return total_len; +} + + +/** + * Deserialize an credential list + * + * @param data the serialized attribute list + * @param data_size the length of the serialized data + * @return a GNUNET_IDENTITY_PROVIDER_AttributeList, must be free'd by caller + */ +struct GNUNET_RECLAIM_CredentialList * +GNUNET_RECLAIM_credential_list_deserialize (const char *data, size_t data_size) +{ + struct GNUNET_RECLAIM_CredentialList *al; + struct GNUNET_RECLAIM_CredentialListEntry *ale; + size_t att_len; + const char *read_ptr; + + al = GNUNET_new (struct GNUNET_RECLAIM_CredentialList); + + if ((data_size < sizeof(struct + Credential) + + sizeof(struct GNUNET_RECLAIM_CredentialListEntry))) + return al; + + read_ptr = data; + while (((data + data_size) - read_ptr) >= sizeof(struct Credential)) + { + ale = GNUNET_new (struct GNUNET_RECLAIM_CredentialListEntry); + ale->credential = + GNUNET_RECLAIM_credential_deserialize (read_ptr, + data_size - (read_ptr - data)); + if (NULL == ale->credential) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to deserialize malformed credential.\n"); + GNUNET_free (ale); + return al; + } + GNUNET_CONTAINER_DLL_insert (al->list_head, al->list_tail, ale); + att_len = GNUNET_RECLAIM_credential_serialize_get_size (ale->credential); + read_ptr += att_len; + } + return al; +} + + +/** + * Make a (deep) copy of the credential list + * @param attrs claim list to copy + * @return copied claim list + */ +struct GNUNET_RECLAIM_CredentialList * +GNUNET_RECLAIM_credential_list_dup ( + const struct GNUNET_RECLAIM_CredentialList *al) +{ + struct GNUNET_RECLAIM_CredentialListEntry *ale; + struct GNUNET_RECLAIM_CredentialListEntry *result_ale; + struct GNUNET_RECLAIM_CredentialList *result; + + result = GNUNET_new (struct GNUNET_RECLAIM_CredentialList); + for (ale = al->list_head; NULL != ale; ale = ale->next) + { + result_ale = GNUNET_new (struct GNUNET_RECLAIM_CredentialListEntry); + GNUNET_assert (NULL != ale->credential); + result_ale->credential = + GNUNET_RECLAIM_credential_new (ale->credential->name, + ale->credential->type, + ale->credential->data, + ale->credential->data_size); + result_ale->credential->id = ale->credential->id; + GNUNET_CONTAINER_DLL_insert (result->list_head, + result->list_tail, + result_ale); + } + return result; +} + + +/** + * Destroy credential list + * + * @param attrs list to destroy + */ +void +GNUNET_RECLAIM_credential_list_destroy ( + struct GNUNET_RECLAIM_CredentialList *al) +{ + struct GNUNET_RECLAIM_CredentialListEntry *ale; + struct GNUNET_RECLAIM_CredentialListEntry *tmp_ale; + + for (ale = al->list_head; NULL != ale;) + { + if (NULL != ale->credential) + GNUNET_free (ale->credential); + tmp_ale = ale; + ale = ale->next; + GNUNET_free (tmp_ale); + } + GNUNET_free (al); +} + + +/** + * Get required size for serialization buffer + * + * @param attr the credential to serialize + * @return the required buffer size + */ +size_t +GNUNET_RECLAIM_credential_serialize_get_size ( + const struct GNUNET_RECLAIM_Credential *credential) +{ + return sizeof(struct Credential) + strlen (credential->name) + + credential->data_size; +} + + +/** + * Serialize an credential + * + * @param attr the credential to serialize + * @param result the serialized credential + * @return length of serialized data + */ +size_t +GNUNET_RECLAIM_credential_serialize ( + const struct GNUNET_RECLAIM_Credential *credential, + char *result) +{ + size_t data_len_ser; + size_t name_len; + struct Credential *atts; + char *write_ptr; + + atts = (struct Credential *) result; + atts->credential_type = htons (credential->type); + atts->credential_flag = htonl (credential->flag); + atts->credential_id = credential->id; + name_len = strlen (credential->name); + atts->name_len = htons (name_len); + write_ptr = (char *) &atts[1]; + GNUNET_memcpy (write_ptr, credential->name, name_len); + write_ptr += name_len; + // TODO plugin-ize + // data_len_ser = plugin->serialize_attribute_value (attr, + // &attr_ser[1]); + data_len_ser = credential->data_size; + GNUNET_memcpy (write_ptr, credential->data, credential->data_size); + atts->data_size = htons (data_len_ser); + + return sizeof(struct Credential) + strlen (credential->name) + + credential->data_size; +} + + +/** + * Deserialize an credential + * + * @param data the serialized credential + * @param data_size the length of the serialized data + * + * @return a GNUNET_IDENTITY_PROVIDER_Attribute, must be free'd by caller + */ +struct GNUNET_RECLAIM_Credential * +GNUNET_RECLAIM_credential_deserialize (const char *data, size_t data_size) +{ + struct GNUNET_RECLAIM_Credential *credential; + struct Credential *atts; + size_t data_len; + size_t name_len; + char *write_ptr; + + if (data_size < sizeof(struct Credential)) + return NULL; + + atts = (struct Credential *) data; + data_len = ntohs (atts->data_size); + name_len = ntohs (atts->name_len); + if (data_size < sizeof(struct Credential) + data_len + name_len) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Buffer too small to deserialize\n"); + return NULL; + } + credential = GNUNET_malloc (sizeof(struct GNUNET_RECLAIM_Credential) + + data_len + name_len + 1); + credential->type = ntohs (atts->credential_type); + credential->flag = ntohl (atts->credential_flag); + credential->id = atts->credential_id; + credential->data_size = data_len; + + write_ptr = (char *) &credential[1]; + GNUNET_memcpy (write_ptr, &atts[1], name_len); + write_ptr[name_len] = '\0'; + credential->name = write_ptr; + + write_ptr += name_len + 1; + GNUNET_memcpy (write_ptr, (char *) &atts[1] + name_len, + credential->data_size); + credential->data = write_ptr; + return credential; +} + + +struct GNUNET_RECLAIM_AttributeList* +GNUNET_RECLAIM_credential_get_attributes (const struct + GNUNET_RECLAIM_Credential *credential) +{ + unsigned int i; + struct Plugin *plugin; + struct GNUNET_RECLAIM_AttributeList *ret; + init (); + for (i = 0; i < num_plugins; i++) + { + plugin = credential_plugins[i]; + if (NULL != + (ret = plugin->api->get_attributes (plugin->api->cls, + credential))) + return ret; + } + return NULL; +} + + +char* +GNUNET_RECLAIM_credential_get_issuer (const struct + GNUNET_RECLAIM_Credential *credential) +{ + unsigned int i; + struct Plugin *plugin; + char *ret; + init (); + for (i = 0; i < num_plugins; i++) + { + plugin = credential_plugins[i]; + if (NULL != + (ret = plugin->api->get_issuer (plugin->api->cls, + credential))) + return ret; + } + return NULL; +} + + +int +GNUNET_RECLAIM_credential_get_expiration (const struct + GNUNET_RECLAIM_Credential *credential, + struct GNUNET_TIME_Absolute*exp) +{ + unsigned int i; + struct Plugin *plugin; + init (); + for (i = 0; i < num_plugins; i++) + { + plugin = credential_plugins[i]; + if (GNUNET_OK != plugin->api->get_expiration (plugin->api->cls, + credential, + exp)) + continue; + return GNUNET_OK; + } + return GNUNET_SYSERR; +} diff --git a/src/reclaim/reclaim_credential.h b/src/reclaim/reclaim_credential.h new file mode 100644 index 000000000..cd3a9539a --- /dev/null +++ b/src/reclaim/reclaim_credential.h @@ -0,0 +1,64 @@ +/* + This file is part of GNUnet. + Copyright (C) 2012-2015 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @author Martin Schanzenbach + * @file reclaim/reclaim_credential.h + * @brief GNUnet reclaim identity attribute credentials + * + */ +#ifndef RECLAIM_CREDENTIAL_H +#define RECLAIM_CREDENTIAL_H + +#include "gnunet_reclaim_service.h" + +/** + * Serialized credential claim + */ +struct Credential +{ + /** + * Credential type + */ + uint32_t credential_type; + + /** + * Credential flag + */ + uint32_t credential_flag; + + /** + * Credential ID + */ + struct GNUNET_RECLAIM_Identifier credential_id; + + /** + * Name length + */ + uint32_t name_len; + + /** + * Data size + */ + uint32_t data_size; + + // followed by data_size Credential value data +}; + +#endif -- cgit v1.2.3 From a57d476abbe857365aff157f389cc1188b5dd090 Mon Sep 17 00:00:00 2001 From: Martin Schanzenbach Date: Wed, 19 Aug 2020 23:53:02 +0200 Subject: reclaim: Attestations now called credentials. Credentials are presented to third parties as presentations. --- src/include/gnunet_gnsrecord_lib.h | 7 +- src/include/gnunet_reclaim_lib.h | 13 +- src/include/gnunet_reclaim_plugin.h | 10 + src/include/gnunet_reclaim_service.h | 18 +- src/reclaim/gnunet-reclaim.c | 5 +- src/reclaim/gnunet-service-reclaim.c | 33 +- src/reclaim/gnunet-service-reclaim_tickets.c | 197 ++++++++--- src/reclaim/gnunet-service-reclaim_tickets.h | 7 +- src/reclaim/json_reclaim.c | 12 +- src/reclaim/json_reclaim.h | 2 +- src/reclaim/oidc_helper.c | 22 +- src/reclaim/oidc_helper.h | 16 +- src/reclaim/plugin_gnsrecord_reclaim.c | 12 +- src/reclaim/plugin_reclaim_credential_jwt.c | 159 +++++++-- src/reclaim/plugin_rest_openid_connect.c | 68 ++-- src/reclaim/plugin_rest_reclaim.c | 6 +- src/reclaim/reclaim.h | 7 + src/reclaim/reclaim_api.c | 58 +++- src/reclaim/reclaim_credential.c | 501 ++++++++++++++++++++++++++- src/reclaim/reclaim_credential.h | 35 ++ 20 files changed, 1010 insertions(+), 178 deletions(-) diff --git a/src/include/gnunet_gnsrecord_lib.h b/src/include/gnunet_gnsrecord_lib.h index c976c89c5..960203fb1 100644 --- a/src/include/gnunet_gnsrecord_lib.h +++ b/src/include/gnunet_gnsrecord_lib.h @@ -143,12 +143,13 @@ extern "C" { /** * Record type for an attribute attestation */ -#define GNUNET_GNSRECORD_TYPE_RECLAIM_ATTESTATION 65554 +#define GNUNET_GNSRECORD_TYPE_RECLAIM_CREDENTIAL 65554 /** - * Record type for an attestation reference in a ticket + * Record type for a presentation of a credential (used + * in a ticket record set) */ -#define GNUNET_GNSRECORD_TYPE_RECLAIM_ATTESTATION_REF 65555 +#define GNUNET_GNSRECORD_TYPE_RECLAIM_PRESENTATION 65555 /** diff --git a/src/include/gnunet_reclaim_lib.h b/src/include/gnunet_reclaim_lib.h index f5c3d3864..bbf1c3ad3 100644 --- a/src/include/gnunet_reclaim_lib.h +++ b/src/include/gnunet_reclaim_lib.h @@ -780,6 +780,11 @@ GNUNET_RECLAIM_presentation_value_to_string (uint32_t type, const void *data, size_t data_size); +struct GNUNET_RECLAIM_Presentation * +GNUNET_RECLAIM_presentation_new (uint32_t type, + const void *data, + size_t data_size); + /** * Convert human-readable version of a 'claim' of a presentation to the binary * representation @@ -828,14 +833,16 @@ GNUNET_RECLAIM_presentation_get_expiration (const struct * * @param cred the credential to use * @param attrs the attributes to present from the credential - * @return the credential presentation presenting the attributes according + * @param presentation the credential presentation presenting the attributes according * to the presentation mechanism of the credential * or NULL on error. + * @return GNUNET_OK on success. */ -struct GNUNET_RECLAIM_Presentation* +int GNUNET_RECLAIM_credential_get_presentation ( const struct GNUNET_RECLAIM_Credential *cred, - const struct GNUNET_RECLAIM_AttributeList *attrs); + const struct GNUNET_RECLAIM_AttributeList *attrs, + struct GNUNET_RECLAIM_Presentation **presentation); #if 0 /* keep Emacsens' auto-indent happy */ diff --git a/src/include/gnunet_reclaim_plugin.h b/src/include/gnunet_reclaim_plugin.h index af6c74e0b..2ba8fc8a0 100644 --- a/src/include/gnunet_reclaim_plugin.h +++ b/src/include/gnunet_reclaim_plugin.h @@ -301,6 +301,11 @@ typedef int (*GNUNET_RECLAIM_PresentationGetExpirationFunction) ( const struct GNUNET_RECLAIM_Presentation *cred, struct GNUNET_TIME_Absolute *expiration); +typedef int (*GNUNET_RECLAIM_CredentialToPresentation) ( + void *cls, + const struct GNUNET_RECLAIM_Credential *cred, + const struct GNUNET_RECLAIM_AttributeList *attrs, + struct GNUNET_RECLAIM_Presentation **presentation); /** * Each plugin is required to return a pointer to a struct of this @@ -416,6 +421,11 @@ struct GNUNET_RECLAIM_CredentialPluginFunctions */ GNUNET_RECLAIM_PresentationGetExpirationFunction get_expiration_p; + /** + * Get presentation + */ + GNUNET_RECLAIM_CredentialToPresentation create_presentation; + }; diff --git a/src/include/gnunet_reclaim_service.h b/src/include/gnunet_reclaim_service.h index 8387c79b0..368058f56 100644 --- a/src/include/gnunet_reclaim_service.h +++ b/src/include/gnunet_reclaim_service.h @@ -92,7 +92,21 @@ struct GNUNET_RECLAIM_Ticket * @param ticket the ticket */ typedef void (*GNUNET_RECLAIM_TicketCallback) ( - void *cls, const struct GNUNET_RECLAIM_Ticket *ticket); + void *cls, + const struct GNUNET_RECLAIM_Ticket *ticket); + +/** + * Method called when a token has been issued. + * On success returns a ticket that can be given to a relying party + * in order for it retrive identity attributes + * + * @param cls closure + * @param ticket the ticket + */ +typedef void (*GNUNET_RECLAIM_IssueTicketCallback) ( + void *cls, + const struct GNUNET_RECLAIM_Ticket *ticket, + const struct GNUNET_RECLAIM_PresentationList *presentations); /** @@ -369,7 +383,7 @@ GNUNET_RECLAIM_ticket_issue ( const struct GNUNET_CRYPTO_EcdsaPrivateKey *iss, const struct GNUNET_CRYPTO_EcdsaPublicKey *rp, const struct GNUNET_RECLAIM_AttributeList *attrs, - GNUNET_RECLAIM_TicketCallback cb, void *cb_cls); + GNUNET_RECLAIM_IssueTicketCallback cb, void *cb_cls); /** diff --git a/src/reclaim/gnunet-reclaim.c b/src/reclaim/gnunet-reclaim.c index a59053f5f..ab281a645 100644 --- a/src/reclaim/gnunet-reclaim.c +++ b/src/reclaim/gnunet-reclaim.c @@ -227,7 +227,9 @@ do_cleanup (void *cls) static void -ticket_issue_cb (void *cls, const struct GNUNET_RECLAIM_Ticket *ticket) +ticket_issue_cb (void *cls, + const struct GNUNET_RECLAIM_Ticket *ticket, + const struct GNUNET_RECLAIM_PresentationList *presentations) { char *ticket_str; @@ -456,6 +458,7 @@ iter_finished (void *cls) if (NULL == attr_to_delete) { fprintf (stdout, "No such attribute ``%s''\n", attr_delete); + GNUNET_SCHEDULER_add_now (&do_cleanup, NULL); return; } reclaim_op = GNUNET_RECLAIM_attribute_delete (reclaim_handle, diff --git a/src/reclaim/gnunet-service-reclaim.c b/src/reclaim/gnunet-service-reclaim.c index 30a84b3e8..d2cdc62a2 100644 --- a/src/reclaim/gnunet-service-reclaim.c +++ b/src/reclaim/gnunet-service-reclaim.c @@ -646,19 +646,33 @@ static void send_ticket_result (const struct IdpClient *client, uint32_t r_id, const struct GNUNET_RECLAIM_Ticket *ticket, + const struct GNUNET_RECLAIM_PresentationList *presentations, uint32_t success) { struct TicketResultMessage *irm; struct GNUNET_MQ_Envelope *env; + size_t pres_len = 0; - env = GNUNET_MQ_msg (irm, - GNUNET_MESSAGE_TYPE_RECLAIM_TICKET_RESULT); + if (NULL != presentations) + { + pres_len = + GNUNET_RECLAIM_presentation_list_serialize_get_size (presentations); + } + env = GNUNET_MQ_msg_extra (irm, + pres_len, + GNUNET_MESSAGE_TYPE_RECLAIM_TICKET_RESULT); if (NULL != ticket) { irm->ticket = *ticket; } // TODO add success member irm->id = htonl (r_id); + irm->presentations_len = htons (pres_len); + if (NULL != presentations) + { + GNUNET_RECLAIM_presentation_list_serialize (presentations, + (char*) &irm[1]); + } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending TICKET_RESULT message\n"); GNUNET_MQ_send (client->mq, env); } @@ -669,12 +683,14 @@ send_ticket_result (const struct IdpClient *client, * * @param cls out ticket issue operation handle * @param ticket the issued ticket + * @param presentations newly created credential presentations (NULL on error) * @param success issue success status (GNUNET_OK if successful) * @param emsg error message (NULL of success is GNUNET_OK) */ static void issue_ticket_result_cb (void *cls, struct GNUNET_RECLAIM_Ticket *ticket, + struct GNUNET_RECLAIM_PresentationList *presentations, int32_t success, const char *emsg) { @@ -682,7 +698,7 @@ issue_ticket_result_cb (void *cls, if (GNUNET_OK != success) { - send_ticket_result (tio->client, tio->r_id, NULL, GNUNET_SYSERR); + send_ticket_result (tio->client, tio->r_id, NULL, NULL, GNUNET_SYSERR); GNUNET_CONTAINER_DLL_remove (tio->client->issue_op_head, tio->client->issue_op_tail, tio); @@ -690,7 +706,8 @@ issue_ticket_result_cb (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error issuing ticket: %s\n", emsg); return; } - send_ticket_result (tio->client, tio->r_id, ticket, GNUNET_SYSERR); + send_ticket_result (tio->client, tio->r_id, + ticket, presentations, GNUNET_SYSERR); GNUNET_CONTAINER_DLL_remove (tio->client->issue_op_head, tio->client->issue_op_tail, tio); @@ -871,7 +888,7 @@ consume_result_cb (void *cls, GNUNET_MESSAGE_TYPE_RECLAIM_CONSUME_TICKET_RESULT); crm->id = htonl (cop->r_id); crm->attrs_len = htons (attrs_len); - crm->pres_len = htons (pres_len); + crm->presentations_len = htons (pres_len); crm->identity = *identity; crm->result = htonl (success); data_tmp = (char *) &crm[1]; @@ -1152,7 +1169,7 @@ cred_add_cb (void *cls, buf_size = GNUNET_RECLAIM_credential_serialize_get_size (ash->credential); buf = GNUNET_malloc (buf_size); - GNUNET_RECLAIM_presentation_serialize (ash->credential, buf); + GNUNET_RECLAIM_credential_serialize (ash->credential, buf); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Storing new credential under `%s'.\n", label); @@ -1611,8 +1628,8 @@ cred_delete_cont (void *cls, int32_t success, const char *emsg) * @dam message to check */ static int -check_cred_delete_message (void *cls, - const struct AttributeDeleteMessage *dam) +check_credential_delete_message (void *cls, + const struct AttributeDeleteMessage *dam) { uint16_t size; diff --git a/src/reclaim/gnunet-service-reclaim_tickets.c b/src/reclaim/gnunet-service-reclaim_tickets.c index 18b173aaa..4dd8100f9 100644 --- a/src/reclaim/gnunet-service-reclaim_tickets.c +++ b/src/reclaim/gnunet-service-reclaim_tickets.c @@ -114,9 +114,9 @@ struct RECLAIM_TICKETS_ConsumeHandle struct GNUNET_RECLAIM_AttributeList *attrs; /** - * Credentials + * Presentations */ - struct GNUNET_RECLAIM_CredentialList *credentials; + struct GNUNET_RECLAIM_PresentationList *presentations; /** * Lookup time @@ -172,6 +172,11 @@ struct TicketIssueHandle */ struct GNUNET_RECLAIM_AttributeList *attrs; + /** + * Presentations to add + */ + struct GNUNET_RECLAIM_PresentationList *presentations; + /** * Issuer Key */ @@ -981,8 +986,8 @@ cleanup_cth (struct RECLAIM_TICKETS_ConsumeHandle *cth) if (NULL != cth->attrs) GNUNET_RECLAIM_attribute_list_destroy (cth->attrs); - if (NULL != cth->credentials) - GNUNET_RECLAIM_credential_list_destroy (cth->credentials); + if (NULL != cth->presentations) + GNUNET_RECLAIM_presentation_list_destroy (cth->presentations); GNUNET_free (cth); } @@ -1040,7 +1045,7 @@ process_parallel_lookup_result (void *cls, return; // Wait for more /* Else we are done */ cth->cb (cth->cb_cls, &cth->ticket.identity, - cth->attrs, cth->credentials, GNUNET_OK, NULL); + cth->attrs, cth->presentations, GNUNET_OK, NULL); cleanup_cth (cth); } @@ -1090,6 +1095,7 @@ lookup_authz_cb (void *cls, struct RECLAIM_TICKETS_ConsumeHandle *cth = cls; struct ParallelLookup *parallel_lookup; char *lbl; + struct GNUNET_RECLAIM_PresentationListEntry *ale; cth->lookup_request = NULL; @@ -1113,13 +1119,12 @@ lookup_authz_cb (void *cls, switch (rd[i].record_type) { case GNUNET_GNSRECORD_TYPE_RECLAIM_PRESENTATION: - struct GNUNET_RECLAIM_CredentialListEntry *ale; - ale = GNUNET_new (struct GNUNET_RECLAIM_CredentialListEntry); - ale->credential = - GNUNET_RECLAIM_credential_deserialize (rd[i].data, - rd[i].data_size); - GNUNET_CONTAINER_DLL_insert (cth->credentials->list_head, - cth->credentials->list_tail, + ale = GNUNET_new (struct GNUNET_RECLAIM_PresentationListEntry); + ale->presentation = + GNUNET_RECLAIM_presentation_deserialize (rd[i].data, + rd[i].data_size); + GNUNET_CONTAINER_DLL_insert (cth->presentations->list_head, + cth->presentations->list_tail, ale); break; case GNUNET_GNSRECORD_TYPE_RECLAIM_ATTRIBUTE_REF: @@ -1162,7 +1167,7 @@ lookup_authz_cb (void *cls, * No references found, return empty attribute list */ cth->cb (cth->cb_cls, &cth->ticket.identity, - cth->attrs, cth->credentials, GNUNET_OK, NULL); + cth->attrs, NULL, GNUNET_OK, NULL); cleanup_cth (cth); } @@ -1192,7 +1197,7 @@ RECLAIM_TICKETS_consume (const struct GNUNET_CRYPTO_EcdsaPrivateKey *id, cth->identity = *id; GNUNET_CRYPTO_ecdsa_key_get_public (&cth->identity, &cth->identity_pub); cth->attrs = GNUNET_new (struct GNUNET_RECLAIM_AttributeList); - cth->credentials = GNUNET_new (struct GNUNET_RECLAIM_CredentialList); + cth->presentations = GNUNET_new (struct GNUNET_RECLAIM_PresentationList); cth->ticket = *ticket; cth->cb = cb; cth->cb_cls = cb_cls; @@ -1230,8 +1235,8 @@ RECLAIM_TICKETS_consume_cancel (struct RECLAIM_TICKETS_ConsumeHandle *cth) /******************************* -* Ticket issue -*******************************/ + * Ticket issue + *******************************/ /** * Cleanup ticket consume handle @@ -1264,11 +1269,15 @@ store_ticket_issue_cont (void *cls, int32_t success, const char *emsg) { handle->cb (handle->cb_cls, &handle->ticket, + NULL, GNUNET_SYSERR, "Error storing AuthZ ticket in GNS"); return; } - handle->cb (handle->cb_cls, &handle->ticket, GNUNET_OK, NULL); + handle->cb (handle->cb_cls, + &handle->ticket, + handle->presentations, + GNUNET_OK, NULL); cleanup_issue_handle (handle); } @@ -1284,15 +1293,17 @@ static void issue_ticket (struct TicketIssueHandle *ih) { struct GNUNET_RECLAIM_AttributeListEntry *le; + struct GNUNET_RECLAIM_PresentationListEntry *ple; struct GNUNET_GNSRECORD_Data *attrs_record; char *label; int i; + int j; int attrs_count = 0; for (le = ih->attrs->list_head; NULL != le; le = le->next) attrs_count++; - // Worst case we have one credential per attribute + // Worst case we have one presentation per attribute attrs_record = GNUNET_malloc (2 * attrs_count * sizeof(struct GNUNET_GNSRECORD_Data)); i = 0; @@ -1309,31 +1320,65 @@ issue_ticket (struct TicketIssueHandle *ih) i++; if (GNUNET_NO == GNUNET_RECLAIM_id_is_zero (&le->attribute->credential)) { - struct GNUNET_RECLAIM_Presentation *pres; - int j; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Attribute is backed by credential. Adding...\n"); + struct GNUNET_RECLAIM_Presentation *pres = NULL; for (j = 0; j < i; j++) { if (attrs_record[j].record_type != GNUNET_GNSRECORD_TYPE_RECLAIM_PRESENTATION) continue; - pres = attrs_record[j].data; - if (0 == memcmp (pres->credential_id, + pres = GNUNET_RECLAIM_presentation_deserialize (attrs_record[j].data, + attrs_record[j]. + data_size); + if (NULL == pres) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to deserialize presentation\n"); + continue; + } + if (0 == memcmp (&pres->credential_id, &le->attribute->credential, sizeof (le->attribute->credential))) break; + GNUNET_free (pres); + pres = NULL; } - if (j < i) + if (NULL != pres) + { + GNUNET_free (pres); continue; // Skip as we have already added this credential presentation. - /** - * FIXME: Create a new presentation from the credential. - */ - attrs_record[i].data = &le->attribute->credential; - attrs_record[i].data_size = sizeof(le->attribute->credential); - attrs_record[i].expiration_time = ticket_refresh_interval.rel_value_us; - attrs_record[i].record_type = - GNUNET_GNSRECORD_TYPE_RECLAIM_PRESENTATION; - attrs_record[i].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; - i++; + } + for (ple = ih->presentations->list_head; NULL != ple; ple = ple->next) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Checking presentation....\n"); + + if (0 != memcmp (&le->attribute->credential, + &ple->presentation->credential_id, + sizeof (le->attribute->credential))) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Presentation does not match credential ID.\n"); + continue; + } + char *pres_buf; + size_t pres_size; + pres_size = + GNUNET_RECLAIM_presentation_serialize_get_size (ple->presentation); + pres_buf = GNUNET_malloc (pres_size); + GNUNET_RECLAIM_presentation_serialize (ple->presentation, + pres_buf); + attrs_record[i].data = pres_buf; + attrs_record[i].data_size = pres_size; + attrs_record[i].expiration_time = + ticket_refresh_interval.rel_value_us; + attrs_record[i].record_type = + GNUNET_GNSRECORD_TYPE_RECLAIM_PRESENTATION; + attrs_record[i].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; + i++; + break; + } } } attrs_record[i].data = &ih->ticket; @@ -1355,14 +1400,23 @@ issue_ticket (struct TicketIssueHandle *ih) attrs_record, &store_ticket_issue_cont, ih); + for (j = 0; j > i; j++) + { + if (attrs_record[j].record_type + != GNUNET_GNSRECORD_TYPE_RECLAIM_PRESENTATION) + continue; + // Yes, we are allowed to do this because we allocated it above + char *ptr = (char*) attrs_record[j].data; + GNUNET_free (ptr); + } GNUNET_free (attrs_record); GNUNET_free (label); } /************************************************* -* Ticket iteration (finding a specific ticket) -*************************************************/ + * Ticket iteration (finding a specific ticket) + *************************************************/ /** @@ -1378,6 +1432,7 @@ filter_tickets_error_cb (void *cls) tih->ns_it = NULL; tih->cb (tih->cb_cls, &tih->ticket, + NULL, GNUNET_SYSERR, "Error storing AuthZ ticket in GNS"); cleanup_issue_handle (tih); @@ -1406,11 +1461,12 @@ filter_tickets_cb (void *cls, struct TicketIssueHandle *tih = cls; struct GNUNET_RECLAIM_Ticket *ticket = NULL; struct GNUNET_RECLAIM_Presentation *pres; - - // figure out the number of requested attributes + struct GNUNET_RECLAIM_PresentationList *ticket_presentations; + struct GNUNET_RECLAIM_Credential *cred; + struct GNUNET_RECLAIM_PresentationListEntry *ple; struct GNUNET_RECLAIM_AttributeListEntry *le; unsigned int attr_cnt = 0; - unsigned int cred_cnt = 0; + unsigned int pres_cnt = 0; for (le = tih->attrs->list_head; NULL != le; le = le->next) { @@ -1422,6 +1478,7 @@ filter_tickets_cb (void *cls, // ticket search unsigned int found_attrs_cnt = 0; unsigned int found_pres_cnt = 0; + ticket_presentations = GNUNET_new (struct GNUNET_RECLAIM_PresentationList); for (int i = 0; i < rd_count; i++) { @@ -1450,15 +1507,65 @@ filter_tickets_cb (void *cls, found_attrs_cnt++; } } + if (GNUNET_GNSRECORD_TYPE_RECLAIM_CREDENTIAL == rd[i].record_type) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Found credential...\n"); + + for (le = tih->attrs->list_head; NULL != le; le = le->next) + { + cred = GNUNET_RECLAIM_credential_deserialize (rd[i].data, + rd[i].data_size); + if (GNUNET_YES != GNUNET_RECLAIM_id_is_equal (&cred->id, + &le->attribute->credential)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "No match.\n"); + GNUNET_free (cred); + continue; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Match, creating presentation...\n"); + if (GNUNET_OK != GNUNET_RECLAIM_credential_get_presentation ( + cred, + tih->attrs, + &pres)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unable to retrieve presentation from credential\n"); + GNUNET_free (cred); + continue; + } + ple = GNUNET_new (struct GNUNET_RECLAIM_PresentationListEntry); + ple->presentation = pres; + GNUNET_CONTAINER_DLL_insert (tih->presentations->list_head, + tih->presentations->list_tail, + ple); + GNUNET_free (cred); + } + } if (GNUNET_GNSRECORD_TYPE_RECLAIM_PRESENTATION == rd[i].record_type) { for (le = tih->attrs->list_head; NULL != le; le = le->next) { - pres = rd[i].data; - if (GNUNET_YES == GNUNET_RECLAIM_id_is_equal (pres->credential_id, + pres = GNUNET_RECLAIM_presentation_deserialize (rd[i].data, + rd[i].data_size); + if (NULL == pres) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to deserialize presentation\n"); + continue; + } + if (GNUNET_YES == GNUNET_RECLAIM_id_is_equal (&pres->credential_id, &le->attribute->credential)) + { found_pres_cnt++; - // FIXME should we store credentials here for later use?? + ple = GNUNET_new (struct GNUNET_RECLAIM_PresentationListEntry); + ple->presentation = pres; + GNUNET_CONTAINER_DLL_insert (ticket_presentations->list_head, + ticket_presentations->list_tail, + ple); + } } } } @@ -1472,7 +1579,8 @@ filter_tickets_cb (void *cls, (NULL != ticket)) { GNUNET_NAMESTORE_zone_iteration_stop (tih->ns_it); - tih->cb (tih->cb_cls, &tih->ticket, GNUNET_OK, NULL); + tih->cb (tih->cb_cls, &tih->ticket, ticket_presentations, GNUNET_OK, NULL); + GNUNET_RECLAIM_presentation_list_destroy (ticket_presentations); cleanup_issue_handle (tih); return; } @@ -1524,6 +1632,7 @@ RECLAIM_TICKETS_issue (const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity, tih->cb = cb; tih->cb_cls = cb_cls; tih->attrs = GNUNET_RECLAIM_attribute_list_dup (attrs); + tih->presentations = GNUNET_new (struct GNUNET_RECLAIM_PresentationList); tih->identity = *identity; tih->ticket.audience = *audience; @@ -1541,8 +1650,8 @@ RECLAIM_TICKETS_issue (const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity, /************************************ -* Ticket iteration -************************************/ + * Ticket iteration + ************************************/ /** * Cleanup ticket iterator diff --git a/src/reclaim/gnunet-service-reclaim_tickets.h b/src/reclaim/gnunet-service-reclaim_tickets.h index 404b9c837..0dd790fc7 100644 --- a/src/reclaim/gnunet-service-reclaim_tickets.h +++ b/src/reclaim/gnunet-service-reclaim_tickets.h @@ -113,6 +113,7 @@ typedef void (*RECLAIM_TICKETS_TicketIter) ( * * @param cls closure * @param ticket the ticket + * @param presentations new presentations for ticket (NULL on error) * @param success #GNUNET_SYSERR on failure (including timeout/queue * drop/failure to validate) #GNUNET_OK on success * @param emsg NULL on success, otherwise an error message @@ -120,6 +121,7 @@ typedef void (*RECLAIM_TICKETS_TicketIter) ( typedef void (*RECLAIM_TICKETS_TicketResult) ( void *cls, struct GNUNET_RECLAIM_Ticket *ticket, + struct GNUNET_RECLAIM_PresentationList *presentations, int32_t success, const char *emsg); @@ -129,7 +131,8 @@ typedef void (*RECLAIM_TICKETS_TicketResult) ( * * @param cls closure * @param identity the issuer of the ticket/attributes - * @param l attribute list retrieved through ticket + * @param attributes attribute list retrieved through ticket + * @param presentations attribute presentations (may be NULL) * @param success GNUNET_OK on success * @param emsg error message (NULL on success) */ @@ -137,7 +140,7 @@ typedef void (*RECLAIM_TICKETS_ConsumeCallback) ( void *cls, const struct GNUNET_CRYPTO_EcdsaPublicKey *identity, const struct GNUNET_RECLAIM_AttributeList *attributes, - const struct GNUNET_RECLAIM_CredentialList *credentials, + const struct GNUNET_RECLAIM_PresentationList *presentations, int32_t success, const char *emsg); diff --git a/src/reclaim/json_reclaim.c b/src/reclaim/json_reclaim.c index c8b944326..8a3479b8a 100644 --- a/src/reclaim/json_reclaim.c +++ b/src/reclaim/json_reclaim.c @@ -335,16 +335,16 @@ parse_credential (void *cls, json_t *root, struct GNUNET_JSON_Specification *spe GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Credential value invalid!\n"); return GNUNET_SYSERR; } - attr = GNUNET_RECLAIM_credential_new (name_str, type, data, data_size); + cred = GNUNET_RECLAIM_credential_new (name_str, type, data, data_size); if ((NULL == id_str) || (0 == strlen (id_str))) - memset (&attr->id, 0, sizeof (attr->id)); + memset (&cred->id, 0, sizeof (cred->id)); else GNUNET_STRINGS_string_to_data (id_str, strlen (id_str), - &attr->id, - sizeof(attr->id)); + &cred->id, + sizeof(cred->id)); - *(struct GNUNET_RECLAIM_Credential **) spec->ptr = attr; + *(struct GNUNET_RECLAIM_Credential **) spec->ptr = cred; return GNUNET_OK; } @@ -383,7 +383,7 @@ GNUNET_RECLAIM_JSON_spec_credential (struct .cleaner = &clean_credential, .cls = NULL, .field = NULL, - .ptr = attr, + .ptr = cred, .ptr_size = 0, .size_ptr = NULL }; diff --git a/src/reclaim/json_reclaim.h b/src/reclaim/json_reclaim.h index 8911cf92d..613ddf873 100644 --- a/src/reclaim/json_reclaim.h +++ b/src/reclaim/json_reclaim.h @@ -54,4 +54,4 @@ GNUNET_RECLAIM_JSON_spec_ticket (struct GNUNET_RECLAIM_Ticket **ticket); * @return JSON Specification */ struct GNUNET_JSON_Specification -GNUNET_RECLAIM_JSON_spec_credential (struct GNUNET_RECLAIM_Attestation **cred); +GNUNET_RECLAIM_JSON_spec_credential (struct GNUNET_RECLAIM_Credential **cred); diff --git a/src/reclaim/oidc_helper.c b/src/reclaim/oidc_helper.c index 9c7e79c41..b307a358c 100644 --- a/src/reclaim/oidc_helper.c +++ b/src/reclaim/oidc_helper.c @@ -156,8 +156,8 @@ fix_base64 (char *str) static json_t* generate_userinfo_json(const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, - struct GNUNET_RECLAIM_AttributeList *attrs, - struct GNUNET_RECLAIM_PresentationList *presentations) + const struct GNUNET_RECLAIM_AttributeList *attrs, + const struct GNUNET_RECLAIM_PresentationList *presentations) { struct GNUNET_RECLAIM_AttributeListEntry *le; struct GNUNET_RECLAIM_PresentationListEntry *ple; @@ -296,8 +296,8 @@ generate_userinfo_json(const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, */ char * OIDC_generate_userinfo (const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, - struct GNUNET_RECLAIM_AttributeList *attrs, - struct GNUNET_RECLAIM_PresentationList *presentations) + const struct GNUNET_RECLAIM_AttributeList *attrs, + const struct GNUNET_RECLAIM_PresentationList *presentations) { char *body_str; json_t* body = generate_userinfo_json (sub_key, @@ -323,8 +323,8 @@ OIDC_generate_userinfo (const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, char * OIDC_generate_id_token (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, - struct GNUNET_RECLAIM_AttributeList *attrs, - struct GNUNET_RECLAIM_PresentationList *presentations, + const struct GNUNET_RECLAIM_AttributeList *attrs, + const struct GNUNET_RECLAIM_PresentationList *presentations, const struct GNUNET_TIME_Relative *expiration_time, const char *nonce, const char *secret_key) @@ -440,8 +440,8 @@ OIDC_generate_id_token (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, char * OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer, const struct GNUNET_RECLAIM_Ticket *ticket, - struct GNUNET_RECLAIM_AttributeList *attrs, - struct GNUNET_RECLAIM_PresentationList *presentations, + const struct GNUNET_RECLAIM_AttributeList *attrs, + const struct GNUNET_RECLAIM_PresentationList *presentations, const char *nonce_str, const char *code_challenge) { @@ -596,7 +596,7 @@ OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPublicKey *audience, struct GNUNET_CRYPTO_EcdsaSignature *signature; uint32_t code_challenge_len; uint32_t attrs_ser_len; - uint32_t presentations_ser_len; + uint32_t pres_ser_len; size_t plaintext_len; size_t code_payload_len; uint32_t nonce_len = 0; @@ -702,8 +702,8 @@ OIDC_parse_authz_code (const struct GNUNET_CRYPTO_EcdsaPublicKey *audience, presentations_ser = ((char*) attrs_ser) + attrs_ser_len; pres_ser_len = ntohl (params->pres_list_len); *presentations = - GNUNET_RECLAIM_presentations_list_deserialize (presentations_ser, - pres_ser_len); + GNUNET_RECLAIM_presentation_list_deserialize (presentations_ser, + pres_ser_len); GNUNET_free (code_payload); return GNUNET_OK; diff --git a/src/reclaim/oidc_helper.h b/src/reclaim/oidc_helper.h index 789a2acc7..10a6f3d1f 100644 --- a/src/reclaim/oidc_helper.h +++ b/src/reclaim/oidc_helper.h @@ -52,8 +52,8 @@ char* OIDC_generate_id_token (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, - struct GNUNET_RECLAIM_AttributeList *attrs, - struct GNUNET_RECLAIM_PresentationList *presentations, + const struct GNUNET_RECLAIM_AttributeList *attrs, + const struct GNUNET_RECLAIM_PresentationList *presentations, const struct GNUNET_TIME_Relative *expiration_time, const char *nonce, const char *secret_key); @@ -73,8 +73,8 @@ OIDC_generate_id_token (const struct GNUNET_CRYPTO_EcdsaPublicKey *aud_key, char* OIDC_build_authz_code (const struct GNUNET_CRYPTO_EcdsaPrivateKey *issuer, const struct GNUNET_RECLAIM_Ticket *ticket, - struct GNUNET_RECLAIM_AttributeList *attrs, - struct GNUNET_RECLAIM_PresentationList *presentations, + const struct GNUNET_RECLAIM_AttributeList *attrs, + const struct GNUNET_RECLAIM_PresentationList *presentations, const char *nonce, const char *code_challenge); @@ -139,8 +139,8 @@ OIDC_access_token_parse (const char* token, * @return GNUNET_YES if attribute is implcitly requested */ enum GNUNET_GenericReturnValue -OIDC_check_scopes_for_claim_request (const char*scopes, - const char*attr); +OIDC_check_scopes_for_claim_request (const char *scopes, + const char *attr); /** @@ -153,7 +153,7 @@ OIDC_check_scopes_for_claim_request (const char*scopes, */ char * OIDC_generate_userinfo (const struct GNUNET_CRYPTO_EcdsaPublicKey *sub_key, - struct GNUNET_RECLAIM_AttributeList *attrs, - struct GNUNET_RECLAIM_PresentationList *presentations); + const struct GNUNET_RECLAIM_AttributeList *attrs, + const struct GNUNET_RECLAIM_PresentationList *presentations); #endif diff --git a/src/reclaim/plugin_gnsrecord_reclaim.c b/src/reclaim/plugin_gnsrecord_reclaim.c index b91e123a3..60c49fd6a 100644 --- a/src/reclaim/plugin_gnsrecord_reclaim.c +++ b/src/reclaim/plugin_gnsrecord_reclaim.c @@ -51,8 +51,8 @@ value_to_string (void *cls, uint32_t type, const void *data, size_t data_size) case GNUNET_GNSRECORD_TYPE_RECLAIM_ATTRIBUTE_REF: case GNUNET_GNSRECORD_TYPE_RECLAIM_TICKET: case GNUNET_GNSRECORD_TYPE_RECLAIM_MASTER: - case GNUNET_GNSRECORD_TYPE_RECLAIM_ATTESTATION: - case GNUNET_GNSRECORD_TYPE_RECLAIM_ATTESTATION_REF: + case GNUNET_GNSRECORD_TYPE_RECLAIM_CREDENTIAL: + case GNUNET_GNSRECORD_TYPE_RECLAIM_PRESENTATION: return GNUNET_STRINGS_data_to_string_alloc (data, data_size); default: @@ -89,8 +89,8 @@ string_to_value (void *cls, uint32_t type, const char *s, void **data, case GNUNET_GNSRECORD_TYPE_RECLAIM_ATTRIBUTE_REF: case GNUNET_GNSRECORD_TYPE_RECLAIM_MASTER: case GNUNET_GNSRECORD_TYPE_RECLAIM_TICKET: - case GNUNET_GNSRECORD_TYPE_RECLAIM_ATTESTATION: - case GNUNET_GNSRECORD_TYPE_RECLAIM_ATTESTATION_REF: + case GNUNET_GNSRECORD_TYPE_RECLAIM_CREDENTIAL: + case GNUNET_GNSRECORD_TYPE_RECLAIM_PRESENTATION: return GNUNET_STRINGS_string_to_data (s, strlen (s), *data, *data_size); default: @@ -110,8 +110,8 @@ static struct } name_map[] = { { "RECLAIM_ATTRIBUTE", GNUNET_GNSRECORD_TYPE_RECLAIM_ATTRIBUTE }, { "RECLAIM_ATTRIBUTE_REF", GNUNET_GNSRECORD_TYPE_RECLAIM_ATTRIBUTE_REF }, - { "RECLAIM_ATTESTATION", GNUNET_GNSRECORD_TYPE_RECLAIM_ATTESTATION }, - { "RECLAIM_ATTESTATION_REF", GNUNET_GNSRECORD_TYPE_RECLAIM_ATTESTATION_REF }, + { "RECLAIM_CREDENTIAL", GNUNET_GNSRECORD_TYPE_RECLAIM_CREDENTIAL }, + { "RECLAIM_PRESENTATION", GNUNET_GNSRECORD_TYPE_RECLAIM_PRESENTATION }, { "RECLAIM_MASTER", GNUNET_GNSRECORD_TYPE_RECLAIM_MASTER }, { "RECLAIM_OIDC_CLIENT", GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_CLIENT }, { "RECLAIM_OIDC_REDIRECT", GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT }, diff --git a/src/reclaim/plugin_reclaim_credential_jwt.c b/src/reclaim/plugin_reclaim_credential_jwt.c index 38effcf78..148865223 100644 --- a/src/reclaim/plugin_reclaim_credential_jwt.c +++ b/src/reclaim/plugin_reclaim_credential_jwt.c @@ -48,7 +48,7 @@ jwt_value_to_string (void *cls, { switch (type) { - case GNUNET_RECLAIM_credential_TYPE_JWT: + case GNUNET_RECLAIM_CREDENTIAL_TYPE_JWT: return GNUNET_strndup (data, data_size); default: @@ -79,7 +79,7 @@ jwt_string_to_value (void *cls, return GNUNET_SYSERR; switch (type) { - case GNUNET_RECLAIM_credential_TYPE_JWT: + case GNUNET_RECLAIM_CREDENTIAL_TYPE_JWT: *data = GNUNET_strdup (s); *data_size = strlen (s); return GNUNET_OK; @@ -98,8 +98,8 @@ static struct { const char *name; uint32_t number; -} jwt_cred_name_map[] = { { "JWT", GNUNET_RECLAIM_credential_TYPE_JWT }, - { NULL, UINT32_MAX } }; +} jwt_cred_name_map[] = { { "JWT", GNUNET_RECLAIM_CREDENTIAL_TYPE_JWT }, + { NULL, UINT32_MAX } }; /** * Convert a type name to the corresponding number. @@ -135,8 +135,8 @@ jwt_number_to_typename (void *cls, uint32_t type) i = 0; while ((NULL != jwt_cred_name_map[i].name) && (type != - jwt_cred_name_map[i]. - number)) + jwt_cred_name_map[i]. + number)) i++; return jwt_cred_name_map[i].name; } @@ -151,7 +151,7 @@ jwt_number_to_typename (void *cls, uint32_t type) */ struct GNUNET_RECLAIM_AttributeList * jwt_parse_attributes (void *cls, - const struct GNUNET_RECLAIM_Credential *cred) + const char *data) { char *jwt_string; struct GNUNET_RECLAIM_AttributeList *attrs; @@ -162,17 +162,14 @@ jwt_parse_attributes (void *cls, json_t *json_val; json_error_t *json_err = NULL; - /* GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "%s\n", cred->data); (not OK: 'data' is not defined as 0-terminated text, but binary) */ - if (GNUNET_RECLAIM_credential_TYPE_JWT != cred->type) - return NULL; attrs = GNUNET_new (struct GNUNET_RECLAIM_AttributeList); - jwt_string = GNUNET_strdup (cred->data); + jwt_string = GNUNET_strdup (data); const char *jwt_body = strtok (jwt_string, delim); jwt_body = strtok (NULL, delim); GNUNET_STRINGS_base64url_decode (jwt_body, strlen (jwt_body), (void **) &decoded_jwt); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n", decoded_jwt); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Decoded JWT: %s\n", decoded_jwt); GNUNET_assert (NULL != decoded_jwt); json_val = json_loads (decoded_jwt, JSON_DECODE_ANY, json_err); const char *key; @@ -202,6 +199,36 @@ jwt_parse_attributes (void *cls, } +/** + * Parse a JWT and return the respective claim value as Attribute + * + * @param cls the plugin + * @param cred the jwt credential + * @return a GNUNET_RECLAIM_Attribute, containing the new value + */ +struct GNUNET_RECLAIM_AttributeList * +jwt_parse_attributes_c (void *cls, + const struct GNUNET_RECLAIM_Credential *cred) +{ + return jwt_parse_attributes (cls, cred->data); +} + + +/** + * Parse a JWT and return the respective claim value as Attribute + * + * @param cls the plugin + * @param cred the jwt credential + * @return a GNUNET_RECLAIM_Attribute, containing the new value + */ +struct GNUNET_RECLAIM_AttributeList * +jwt_parse_attributes_p (void *cls, + const struct GNUNET_RECLAIM_Presentation *cred) +{ + return jwt_parse_attributes (cls, cred->data); +} + + /** * Parse a JWT and return the issuer * @@ -211,7 +238,7 @@ jwt_parse_attributes (void *cls, */ char * jwt_get_issuer (void *cls, - const struct GNUNET_RECLAIM_Credential *cred) + const char *data) { const char *jwt_body; char *jwt_string; @@ -223,9 +250,7 @@ jwt_get_issuer (void *cls, json_t *json_val; json_error_t *json_err = NULL; - if (GNUNET_RECLAIM_credential_TYPE_JWT != cred->type) - return NULL; - jwt_string = GNUNET_strdup (cred->data); + jwt_string = GNUNET_strdup (data); jwt_body = strtok (jwt_string, delim); jwt_body = strtok (NULL, delim); GNUNET_STRINGS_base64url_decode (jwt_body, strlen (jwt_body), @@ -240,6 +265,40 @@ jwt_get_issuer (void *cls, } +/** + * Parse a JWT and return the issuer + * + * @param cls the plugin + * @param cred the jwt credential + * @return a string, containing the isser + */ +char * +jwt_get_issuer_c (void *cls, + const struct GNUNET_RECLAIM_Credential *cred) +{ + if (GNUNET_RECLAIM_CREDENTIAL_TYPE_JWT != cred->type) + return NULL; + return jwt_get_issuer (cls, cred->data); +} + + +/** + * Parse a JWT and return the issuer + * + * @param cls the plugin + * @param cred the jwt credential + * @return a string, containing the isser + */ +char * +jwt_get_issuer_p (void *cls, + const struct GNUNET_RECLAIM_Presentation *cred) +{ + if (GNUNET_RECLAIM_CREDENTIAL_TYPE_JWT != cred->type) + return NULL; + return jwt_get_issuer (cls, cred->data); +} + + /** * Parse a JWT and return the expiration * @@ -249,7 +308,7 @@ jwt_get_issuer (void *cls, */ int jwt_get_expiration (void *cls, - const struct GNUNET_RECLAIM_Credential *cred, + const char *data, struct GNUNET_TIME_Absolute *exp) { const char *jwt_body; @@ -261,9 +320,7 @@ jwt_get_expiration (void *cls, json_t *json_val; json_error_t *json_err = NULL; - if (GNUNET_RECLAIM_credential_TYPE_JWT != cred->type) - return GNUNET_NO; - jwt_string = GNUNET_strdup (cred->data); + jwt_string = GNUNET_strdup (data); jwt_body = strtok (jwt_string, delim); jwt_body = strtok (NULL, delim); GNUNET_STRINGS_base64url_decode (jwt_body, strlen (jwt_body), @@ -278,6 +335,54 @@ jwt_get_expiration (void *cls, } +/** + * Parse a JWT and return the expiration + * + * @param cls the plugin + * @param cred the jwt credential + * @return a string, containing the isser + */ +int +jwt_get_expiration_c (void *cls, + const struct GNUNET_RECLAIM_Credential *cred, + struct GNUNET_TIME_Absolute *exp) +{ + return jwt_get_expiration (cls, cred->data, exp); +} + + +/** + * Parse a JWT and return the expiration + * + * @param cls the plugin + * @param cred the jwt credential + * @return a string, containing the isser + */ +int +jwt_get_expiration_p (void *cls, + const struct GNUNET_RECLAIM_Presentation *cred, + struct GNUNET_TIME_Absolute *exp) +{ + return jwt_get_expiration (cls, cred->data, exp); +} + + +int +jwt_create_presentation (void *cls, + const struct GNUNET_RECLAIM_Credential *cred, + const struct GNUNET_RECLAIM_AttributeList *attrs, + struct GNUNET_RECLAIM_Presentation **pres) +{ + // FIXME sanity checks?? + if (GNUNET_RECLAIM_CREDENTIAL_TYPE_JWT != cred->type) + return GNUNET_NO; + *pres = GNUNET_RECLAIM_presentation_new (GNUNET_RECLAIM_CREDENTIAL_TYPE_JWT, + cred->data, + cred->data_size); + return GNUNET_OK; +} + + /** * Entry point for the plugin. * @@ -294,9 +399,17 @@ libgnunet_plugin_reclaim_credential_jwt_init (void *cls) api->string_to_value = &jwt_string_to_value; api->typename_to_number = &jwt_typename_to_number; api->number_to_typename = &jwt_number_to_typename; - api->get_attributes = &jwt_parse_attributes; - api->get_issuer = &jwt_get_issuer; - api->get_expiration = &jwt_get_expiration; + api->get_attributes = &jwt_parse_attributes_c; + api->get_issuer = &jwt_get_issuer_c; + api->get_expiration = &jwt_get_expiration_c; + api->value_to_string_p = &jwt_value_to_string; + api->string_to_value_p = &jwt_string_to_value; + api->typename_to_number_p = &jwt_typename_to_number; + api->number_to_typename_p = &jwt_number_to_typename; + api->get_attributes_p = &jwt_parse_attributes_p; + api->get_issuer_p = &jwt_get_issuer_p; + api->get_expiration_p = &jwt_get_expiration_p; + api->create_presentation = &jwt_create_presentation; return api; } diff --git a/src/reclaim/plugin_rest_openid_connect.c b/src/reclaim/plugin_rest_openid_connect.c index 5ae6565af..5b0bb2b6f 100644 --- a/src/reclaim/plugin_rest_openid_connect.c +++ b/src/reclaim/plugin_rest_openid_connect.c @@ -439,10 +439,14 @@ struct RequestHandle struct GNUNET_RECLAIM_AttributeList *attr_userinfo_list; /** - * Credential list + * Credentials */ - struct GNUNET_RECLAIM_CredentialList *creds_list; + struct GNUNET_RECLAIM_CredentialList *credentials; + /** + * Presentations + */ + struct GNUNET_RECLAIM_PresentationList *presentations; /** * IDENTITY Operation @@ -590,8 +594,10 @@ cleanup_handle (struct RequestHandle *handle) GNUNET_RECLAIM_attribute_list_destroy (handle->attr_idtoken_list); if (NULL!=handle->attr_userinfo_list) GNUNET_RECLAIM_attribute_list_destroy (handle->attr_userinfo_list); - if (NULL!=handle->creds_list) - GNUNET_RECLAIM_credential_list_destroy (handle->creds_list); + if (NULL!=handle->credentials) + GNUNET_RECLAIM_credential_list_destroy (handle->credentials); + if (NULL!=handle->presentations) + GNUNET_RECLAIM_presentation_list_destroy (handle->presentations); GNUNET_CONTAINER_DLL_remove (requests_head, requests_tail, handle); @@ -934,7 +940,9 @@ oidc_iteration_error (void *cls) * parameter. Otherwise redirects with error */ static void -oidc_ticket_issue_cb (void *cls, const struct GNUNET_RECLAIM_Ticket *ticket) +oidc_ticket_issue_cb (void *cls, + const struct GNUNET_RECLAIM_Ticket *ticket, + const struct GNUNET_RECLAIM_PresentationList *pres) { struct RequestHandle *handle = cls; struct MHD_Response *resp; @@ -957,7 +965,7 @@ oidc_ticket_issue_cb (void *cls, const struct GNUNET_RECLAIM_Ticket *ticket) code_string = OIDC_build_authz_code (&handle->priv_key, &handle->ticket, handle->attr_idtoken_list, - handle->creds_list, + pres, handle->oidc->nonce, handle->oidc->code_challenge); if ((NULL != handle->redirect_prefix) && (NULL != handle->redirect_suffix) && @@ -1086,7 +1094,7 @@ oidc_cred_collect (void *cls, struct GNUNET_RECLAIM_AttributeListEntry *le; struct GNUNET_RECLAIM_CredentialListEntry *ale; - for (ale = handle->creds_list->list_head; NULL != ale; ale = ale->next) + for (ale = handle->credentials->list_head; NULL != ale; ale = ale->next) { if (GNUNET_NO == GNUNET_RECLAIM_id_is_equal (&ale->credential->id, &cred->id)) @@ -1107,8 +1115,8 @@ oidc_cred_collect (void *cls, cred->type, cred->data, cred->data_size); - GNUNET_CONTAINER_DLL_insert (handle->creds_list->list_head, - handle->creds_list->list_tail, + GNUNET_CONTAINER_DLL_insert (handle->credentials->list_head, + handle->credentials->list_tail, ale); } GNUNET_RECLAIM_get_credentials_next (handle->cred_it); @@ -1129,7 +1137,7 @@ oidc_attr_collect_finished_cb (void *cls) GNUNET_SCHEDULER_add_now (&do_redirect_error, handle); return; } - handle->creds_list = GNUNET_new (struct GNUNET_RECLAIM_CredentialList); + handle->credentials = GNUNET_new (struct GNUNET_RECLAIM_CredentialList); handle->cred_it = GNUNET_RECLAIM_get_credentials_start (idp, &handle->priv_key, @@ -1982,7 +1990,7 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle, const struct EgoEntry *ego_entry; struct GNUNET_TIME_Relative expiration_time; struct GNUNET_RECLAIM_AttributeList *cl = NULL; - struct GNUNET_RECLAIM_CredentialList *al = NULL; + struct GNUNET_RECLAIM_PresentationList *pl = NULL; struct GNUNET_RECLAIM_Ticket ticket; struct GNUNET_CRYPTO_EcdsaPublicKey cid; struct GNUNET_HashCode cache_key; @@ -2068,7 +2076,7 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle, // decode code if (GNUNET_OK != OIDC_parse_authz_code (&cid, code, code_verifier, &ticket, - &cl, &al, &nonce)) + &cl, &pl, &nonce)) { handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); handle->edesc = GNUNET_strdup ("invalid code"); @@ -2108,7 +2116,7 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle, id_token = OIDC_generate_id_token (&ticket.audience, &ticket.identity, cl, - al, + pl, &expiration_time, (NULL != nonce) ? nonce : NULL, jwt_secret); @@ -2124,7 +2132,7 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle, MHD_add_response_header (resp, "Content-Type", "application/json"); handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); GNUNET_RECLAIM_attribute_list_destroy (cl); - GNUNET_RECLAIM_credential_list_destroy (al); + GNUNET_RECLAIM_presentation_list_destroy (pl); GNUNET_free (access_token); GNUNET_free (json_response); GNUNET_free (id_token); @@ -2139,11 +2147,11 @@ static void consume_ticket (void *cls, const struct GNUNET_CRYPTO_EcdsaPublicKey *identity, const struct GNUNET_RECLAIM_Attribute *attr, - const struct GNUNET_RECLAIM_Credential *cred) + const struct GNUNET_RECLAIM_Presentation *pres) { struct RequestHandle *handle = cls; struct GNUNET_RECLAIM_AttributeListEntry *ale; - struct GNUNET_RECLAIM_CredentialListEntry *atle; + struct GNUNET_RECLAIM_PresentationListEntry *atle; struct MHD_Response *resp; char *result_str; handle->idp_op = NULL; @@ -2152,7 +2160,7 @@ consume_ticket (void *cls, { result_str = OIDC_generate_userinfo (&handle->ticket.identity, handle->attr_userinfo_list, - handle->creds_list); + handle->presentations); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Userinfo: %s\n", result_str); resp = GNUNET_REST_create_response (result_str); handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); @@ -2172,25 +2180,25 @@ consume_ticket (void *cls, GNUNET_CONTAINER_DLL_insert (handle->attr_userinfo_list->list_head, handle->attr_userinfo_list->list_tail, ale); - if (NULL == cred) + if (NULL == pres) return; - for (atle = handle->creds_list->list_head; NULL != atle; atle = atle->next) + for (atle = handle->presentations->list_head; + NULL != atle; atle = atle->next) { - if (GNUNET_NO == GNUNET_RECLAIM_id_is_equal (&atle->credential->id, - &cred->id)) + if (GNUNET_NO == GNUNET_RECLAIM_id_is_equal (&atle->presentation->credential_id, + &pres->credential_id)) continue; break; /** already in list **/ } if (NULL == atle) { /** Credential matches for attribute, add **/ - atle = GNUNET_new (struct GNUNET_RECLAIM_CredentialListEntry); - atle->credential = GNUNET_RECLAIM_credential_new (cred->name, - cred->type, - cred->data, - cred->data_size); - GNUNET_CONTAINER_DLL_insert (handle->creds_list->list_head, - handle->creds_list->list_tail, + atle = GNUNET_new (struct GNUNET_RECLAIM_PresentationListEntry); + atle->presentation = GNUNET_RECLAIM_presentation_new (pres->type, + pres->data, + pres->data_size); + GNUNET_CONTAINER_DLL_insert (handle->presentations->list_head, + handle->presentations->list_tail, atle); } } @@ -2289,8 +2297,8 @@ userinfo_endpoint (struct GNUNET_REST_RequestHandle *con_handle, privkey = GNUNET_IDENTITY_ego_get_private_key (aud_ego->ego); handle->attr_userinfo_list = GNUNET_new (struct GNUNET_RECLAIM_AttributeList); - handle->creds_list = - GNUNET_new (struct GNUNET_RECLAIM_CredentialList); + handle->presentations = + GNUNET_new (struct GNUNET_RECLAIM_PresentationList); handle->idp_op = GNUNET_RECLAIM_ticket_consume (idp, privkey, diff --git a/src/reclaim/plugin_rest_reclaim.c b/src/reclaim/plugin_rest_reclaim.c index a51d992e1..ff11d2a56 100644 --- a/src/reclaim/plugin_rest_reclaim.c +++ b/src/reclaim/plugin_rest_reclaim.c @@ -474,7 +474,7 @@ add_credential_cont (struct GNUNET_REST_RequestHandle *con_handle, json_t *data_json; json_error_t err; struct GNUNET_JSON_Specification attrspec[] = - { GNUNET_RECLAIM_JSON_spec_claim_cred (&attribute), + { GNUNET_RECLAIM_JSON_spec_credential (&attribute), GNUNET_JSON_spec_end () }; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -811,7 +811,7 @@ add_attribute_cont (struct GNUNET_REST_RequestHandle *con_handle, json_t *data_json; json_error_t err; struct GNUNET_JSON_Specification attrspec[] = - { GNUNET_RECLAIM_JSON_spec_claim (&attribute), GNUNET_JSON_spec_end () }; + { GNUNET_RECLAIM_JSON_spec_attribute (&attribute), GNUNET_JSON_spec_end () }; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding an attribute for %s.\n", @@ -1180,7 +1180,7 @@ static void consume_cont (void *cls, const struct GNUNET_CRYPTO_EcdsaPublicKey *identity, const struct GNUNET_RECLAIM_Attribute *attr, - const struct GNUNET_RECLAIM_Credential *cred) + const struct GNUNET_RECLAIM_Presentation *pres) { struct RequestHandle *handle = cls; char *val_str; diff --git a/src/reclaim/reclaim.h b/src/reclaim/reclaim.h index 6ba9fdcd7..bc7f34365 100644 --- a/src/reclaim/reclaim.h +++ b/src/reclaim/reclaim.h @@ -462,10 +462,17 @@ struct TicketResultMessage */ uint32_t id GNUNET_PACKED; + /** + * Length of new presentations created + */ + uint32_t presentations_len GNUNET_PACKED; + /** * The new ticket */ struct GNUNET_RECLAIM_Ticket ticket; + + /* Followed by the serialized GNUNET_RECLAIM_PresentationList */ }; /** diff --git a/src/reclaim/reclaim_api.c b/src/reclaim/reclaim_api.c index 2cfcbad09..1e0251519 100644 --- a/src/reclaim/reclaim_api.c +++ b/src/reclaim/reclaim_api.c @@ -91,6 +91,11 @@ struct GNUNET_RECLAIM_Operation */ GNUNET_RECLAIM_TicketCallback tr_cb; + /** + * Ticket issue result callback + */ + GNUNET_RECLAIM_IssueTicketCallback ti_cb; + /** * Envelope with the message for this queue entry. */ @@ -866,6 +871,30 @@ handle_credential_result (void *cls, const struct GNUNET_assert (0); } +/** + * Handle an incoming message of type + * #GNUNET_MESSAGE_TYPE_RECLAIM_TICKET_RESULT + * + * @param cls + * @param msg the message we received + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + */ +static int +check_ticket_result (void *cls, const struct TicketResultMessage *msg) +{ + size_t msg_len; + size_t pres_len; + + msg_len = ntohs (msg->header.size); + pres_len = ntohs (msg->presentations_len); + if (msg_len != sizeof(struct TicketResultMessage) + pres_len) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + /** * Handle an incoming message of type @@ -880,8 +909,10 @@ handle_ticket_result (void *cls, const struct TicketResultMessage *msg) struct GNUNET_RECLAIM_Handle *handle = cls; struct GNUNET_RECLAIM_Operation *op; struct GNUNET_RECLAIM_TicketIterator *it; + struct GNUNET_RECLAIM_PresentationList *pres; uint32_t r_id = ntohl (msg->id); static const struct GNUNET_RECLAIM_Ticket ticket; + uint32_t pres_len = ntohs (msg->presentations_len); for (op = handle->op_head; NULL != op; op = op->next) if (op->r_id == r_id) @@ -893,18 +924,25 @@ handle_ticket_result (void *cls, const struct TicketResultMessage *msg) return; if (NULL != op) { + if (0 < pres_len) + pres = GNUNET_RECLAIM_presentation_list_deserialize ((char*)&msg[1], + pres_len); GNUNET_CONTAINER_DLL_remove (handle->op_head, handle->op_tail, op); if (0 == memcmp (&msg->ticket, &ticket, sizeof(struct GNUNET_RECLAIM_Ticket))) { - if (NULL != op->tr_cb) - op->tr_cb (op->cls, NULL); + if (NULL != op->ti_cb) + op->ti_cb (op->cls, NULL, NULL); } else { - if (NULL != op->tr_cb) - op->tr_cb (op->cls, &msg->ticket); + if (NULL != op->ti_cb) + op->ti_cb (op->cls, + &msg->ticket, + (0 < pres_len) ? pres : NULL); } + if (0 < pres_len) + GNUNET_RECLAIM_presentation_list_destroy (pres); free_op (op); return; } @@ -989,10 +1027,10 @@ reconnect (struct GNUNET_RECLAIM_Handle *h) GNUNET_MESSAGE_TYPE_RECLAIM_CREDENTIAL_RESULT, struct CredentialResultMessage, h), - GNUNET_MQ_hd_fixed_size (ticket_result, - GNUNET_MESSAGE_TYPE_RECLAIM_TICKET_RESULT, - struct TicketResultMessage, - h), + GNUNET_MQ_hd_var_size (ticket_result, + GNUNET_MESSAGE_TYPE_RECLAIM_TICKET_RESULT, + struct TicketResultMessage, + h), GNUNET_MQ_hd_var_size (consume_ticket_result, GNUNET_MESSAGE_TYPE_RECLAIM_CONSUME_TICKET_RESULT, struct ConsumeTicketResultMessage, @@ -1506,7 +1544,7 @@ GNUNET_RECLAIM_ticket_issue ( const struct GNUNET_CRYPTO_EcdsaPrivateKey *iss, const struct GNUNET_CRYPTO_EcdsaPublicKey *rp, const struct GNUNET_RECLAIM_AttributeList *attrs, - GNUNET_RECLAIM_TicketCallback cb, + GNUNET_RECLAIM_IssueTicketCallback cb, void *cb_cls) { struct GNUNET_RECLAIM_Operation *op; @@ -1516,7 +1554,7 @@ GNUNET_RECLAIM_ticket_issue ( fprintf (stderr, "Issuing ticket\n"); op = GNUNET_new (struct GNUNET_RECLAIM_Operation); op->h = h; - op->tr_cb = cb; + op->ti_cb = cb; op->cls = cb_cls; op->r_id = h->r_id_gen++; GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, op); diff --git a/src/reclaim/reclaim_credential.c b/src/reclaim/reclaim_credential.c index 10b17ae96..5c8974400 100644 --- a/src/reclaim/reclaim_credential.c +++ b/src/reclaim/reclaim_credential.c @@ -165,9 +165,9 @@ GNUNET_RECLAIM_credential_number_to_typename (uint32_t type) */ int GNUNET_RECLAIM_credential_string_to_value (uint32_t type, - const char *s, - void **data, - size_t *data_size) + const char *s, + void **data, + size_t *data_size) { unsigned int i; struct Plugin *plugin; @@ -197,8 +197,8 @@ GNUNET_RECLAIM_credential_string_to_value (uint32_t type, */ char * GNUNET_RECLAIM_credential_value_to_string (uint32_t type, - const void *data, - size_t data_size) + const void *data, + size_t data_size) { unsigned int i; struct Plugin *plugin; @@ -229,9 +229,9 @@ GNUNET_RECLAIM_credential_value_to_string (uint32_t type, */ struct GNUNET_RECLAIM_Credential * GNUNET_RECLAIM_credential_new (const char *attr_name, - uint32_t type, - const void *data, - size_t data_size) + uint32_t type, + const void *data, + size_t data_size) { struct GNUNET_RECLAIM_Credential *attr; char *write_ptr; @@ -335,7 +335,7 @@ GNUNET_RECLAIM_credential_list_deserialize (const char *data, size_t data_size) ale = GNUNET_new (struct GNUNET_RECLAIM_CredentialListEntry); ale->credential = GNUNET_RECLAIM_credential_deserialize (read_ptr, - data_size - (read_ptr - data)); + data_size - (read_ptr - data)); if (NULL == ale->credential) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, @@ -371,9 +371,9 @@ GNUNET_RECLAIM_credential_list_dup ( GNUNET_assert (NULL != ale->credential); result_ale->credential = GNUNET_RECLAIM_credential_new (ale->credential->name, - ale->credential->type, - ale->credential->data, - ale->credential->data_size); + ale->credential->type, + ale->credential->data, + ale->credential->data_size); result_ale->credential->id = ale->credential->id; GNUNET_CONTAINER_DLL_insert (result->list_head, result->list_tail, @@ -490,7 +490,7 @@ GNUNET_RECLAIM_credential_deserialize (const char *data, size_t data_size) return NULL; } credential = GNUNET_malloc (sizeof(struct GNUNET_RECLAIM_Credential) - + data_len + name_len + 1); + + data_len + name_len + 1); credential->type = ntohs (atts->credential_type); credential->flag = ntohl (atts->credential_flag); credential->id = atts->credential_id; @@ -511,7 +511,7 @@ GNUNET_RECLAIM_credential_deserialize (const char *data, size_t data_size) struct GNUNET_RECLAIM_AttributeList* GNUNET_RECLAIM_credential_get_attributes (const struct - GNUNET_RECLAIM_Credential *credential) + GNUNET_RECLAIM_Credential *credential) { unsigned int i; struct Plugin *plugin; @@ -531,7 +531,7 @@ GNUNET_RECLAIM_credential_get_attributes (const struct char* GNUNET_RECLAIM_credential_get_issuer (const struct - GNUNET_RECLAIM_Credential *credential) + GNUNET_RECLAIM_Credential *credential) { unsigned int i; struct Plugin *plugin; @@ -551,8 +551,8 @@ GNUNET_RECLAIM_credential_get_issuer (const struct int GNUNET_RECLAIM_credential_get_expiration (const struct - GNUNET_RECLAIM_Credential *credential, - struct GNUNET_TIME_Absolute*exp) + GNUNET_RECLAIM_Credential *credential, + struct GNUNET_TIME_Absolute*exp) { unsigned int i; struct Plugin *plugin; @@ -568,3 +568,470 @@ GNUNET_RECLAIM_credential_get_expiration (const struct } return GNUNET_SYSERR; } + + +/** + * Convert an presentation type name to the corresponding number + * + * @param typename name to convert + * @return corresponding number, UINT32_MAX on error + */ +uint32_t +GNUNET_RECLAIM_presentation_typename_to_number (const char *typename) +{ + unsigned int i; + struct Plugin *plugin; + uint32_t ret; + init (); + for (i = 0; i < num_plugins; i++) + { + plugin = credential_plugins[i]; + if (UINT32_MAX != + (ret = plugin->api->typename_to_number_p (plugin->api->cls, + typename))) + return ret; + } + return UINT32_MAX; +} + + +/** + * Convert an presentation type number to the corresponding presentation type string + * + * @param type number of a type + * @return corresponding typestring, NULL on error + */ +const char * +GNUNET_RECLAIM_presentation_number_to_typename (uint32_t type) +{ + unsigned int i; + struct Plugin *plugin; + const char *ret; + + init (); + for (i = 0; i < num_plugins; i++) + { + plugin = credential_plugins[i]; + if (NULL != + (ret = plugin->api->number_to_typename_p (plugin->api->cls, type))) + return ret; + } + return NULL; +} + + +/** + * Convert human-readable version of a 'claim' of an presentation to the binary + * representation + * + * @param type type of the claim + * @param s human-readable string + * @param data set to value in binary encoding (will be allocated) + * @param data_size set to number of bytes in @a data + * @return #GNUNET_OK on success + */ +int +GNUNET_RECLAIM_presentation_string_to_value (uint32_t type, + const char *s, + void **data, + size_t *data_size) +{ + unsigned int i; + struct Plugin *plugin; + + init (); + for (i = 0; i < num_plugins; i++) + { + plugin = credential_plugins[i]; + if (GNUNET_OK == plugin->api->string_to_value_p (plugin->api->cls, + type, + s, + data, + data_size)) + return GNUNET_OK; + } + return GNUNET_SYSERR; +} + + +/** + * Convert the 'claim' of an presentation to a string + * + * @param type the type of presentation + * @param data claim in binary encoding + * @param data_size number of bytes in @a data + * @return NULL on error, otherwise human-readable representation of the claim + */ +char * +GNUNET_RECLAIM_presentation_value_to_string (uint32_t type, + const void *data, + size_t data_size) +{ + unsigned int i; + struct Plugin *plugin; + char *ret; + + init (); + for (i = 0; i < num_plugins; i++) + { + plugin = credential_plugins[i]; + if (NULL != (ret = plugin->api->value_to_string_p (plugin->api->cls, + type, + data, + data_size))) + return ret; + } + return NULL; +} + + +struct GNUNET_RECLAIM_Presentation * +GNUNET_RECLAIM_presentation_new (uint32_t type, + const void *data, + size_t data_size) +{ + struct GNUNET_RECLAIM_Presentation *attr; + char *write_ptr; + + attr = GNUNET_malloc (sizeof(struct GNUNET_RECLAIM_Presentation) + + data_size); + attr->type = type; + attr->data_size = data_size; + write_ptr = (char *) &attr[1]; + GNUNET_memcpy (write_ptr, data, data_size); + attr->data = write_ptr; + return attr; +} + + +/** + * Get required size for serialization buffer + * + * @param attrs the attribute list to serialize + * @return the required buffer size + */ +size_t +GNUNET_RECLAIM_presentation_list_serialize_get_size ( + const struct GNUNET_RECLAIM_PresentationList *presentations) +{ + struct GNUNET_RECLAIM_PresentationListEntry *le; + size_t len = 0; + + for (le = presentations->list_head; NULL != le; le = le->next) + { + GNUNET_assert (NULL != le->presentation); + len += GNUNET_RECLAIM_presentation_serialize_get_size (le->presentation); + len += sizeof(struct GNUNET_RECLAIM_PresentationListEntry); + } + return len; +} + + +/** + * Serialize an attribute list + * + * @param attrs the attribute list to serialize + * @param result the serialized attribute + * @return length of serialized data + */ +size_t +GNUNET_RECLAIM_presentation_list_serialize ( + const struct GNUNET_RECLAIM_PresentationList *presentations, + char *result) +{ + struct GNUNET_RECLAIM_PresentationListEntry *le; + size_t len; + size_t total_len; + char *write_ptr; + write_ptr = result; + total_len = 0; + for (le = presentations->list_head; NULL != le; le = le->next) + { + GNUNET_assert (NULL != le->presentation); + len = GNUNET_RECLAIM_presentation_serialize (le->presentation, write_ptr); + total_len += len; + write_ptr += len; + } + return total_len; +} + + +/** + * Deserialize an presentation list + * + * @param data the serialized attribute list + * @param data_size the length of the serialized data + * @return a GNUNET_IDENTITY_PROVIDER_AttributeList, must be free'd by caller + */ +struct GNUNET_RECLAIM_PresentationList * +GNUNET_RECLAIM_presentation_list_deserialize (const char *data, size_t + data_size) +{ + struct GNUNET_RECLAIM_PresentationList *al; + struct GNUNET_RECLAIM_PresentationListEntry *ale; + size_t att_len; + const char *read_ptr; + + al = GNUNET_new (struct GNUNET_RECLAIM_PresentationList); + + if ((data_size < sizeof(struct Presentation) + + sizeof(struct GNUNET_RECLAIM_PresentationListEntry))) + return al; + + read_ptr = data; + while (((data + data_size) - read_ptr) >= sizeof(struct Presentation)) + { + ale = GNUNET_new (struct GNUNET_RECLAIM_PresentationListEntry); + ale->presentation = + GNUNET_RECLAIM_presentation_deserialize (read_ptr, + data_size - (read_ptr - data)); + if (NULL == ale->presentation) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to deserialize malformed presentation.\n"); + GNUNET_free (ale); + return al; + } + GNUNET_CONTAINER_DLL_insert (al->list_head, al->list_tail, ale); + att_len = GNUNET_RECLAIM_presentation_serialize_get_size ( + ale->presentation); + read_ptr += att_len; + } + return al; +} + + +/** + * Make a (deep) copy of the presentation list + * @param attrs claim list to copy + * @return copied claim list + */ +struct GNUNET_RECLAIM_PresentationList * +GNUNET_RECLAIM_presentation_list_dup ( + const struct GNUNET_RECLAIM_PresentationList *al) +{ + struct GNUNET_RECLAIM_PresentationListEntry *ale; + struct GNUNET_RECLAIM_PresentationListEntry *result_ale; + struct GNUNET_RECLAIM_PresentationList *result; + + result = GNUNET_new (struct GNUNET_RECLAIM_PresentationList); + for (ale = al->list_head; NULL != ale; ale = ale->next) + { + result_ale = GNUNET_new (struct GNUNET_RECLAIM_PresentationListEntry); + GNUNET_assert (NULL != ale->presentation); + result_ale->presentation = + GNUNET_RECLAIM_presentation_new (ale->presentation->type, + ale->presentation->data, + ale->presentation->data_size); + result_ale->presentation->credential_id = ale->presentation->credential_id; + GNUNET_CONTAINER_DLL_insert (result->list_head, + result->list_tail, + result_ale); + } + return result; +} + + +/** + * Destroy presentation list + * + * @param attrs list to destroy + */ +void +GNUNET_RECLAIM_presentation_list_destroy ( + struct GNUNET_RECLAIM_PresentationList *al) +{ + struct GNUNET_RECLAIM_PresentationListEntry *ale; + struct GNUNET_RECLAIM_PresentationListEntry *tmp_ale; + + for (ale = al->list_head; NULL != ale;) + { + if (NULL != ale->presentation) + GNUNET_free (ale->presentation); + tmp_ale = ale; + ale = ale->next; + GNUNET_free (tmp_ale); + } + GNUNET_free (al); +} + + +/** + * Get required size for serialization buffer + * + * @param attr the presentation to serialize + * @return the required buffer size + */ +size_t +GNUNET_RECLAIM_presentation_serialize_get_size ( + const struct GNUNET_RECLAIM_Presentation *presentation) +{ + return sizeof(struct Presentation) + presentation->data_size; +} + + +/** + * Serialize an presentation + * + * @param attr the presentation to serialize + * @param result the serialized presentation + * @return length of serialized data + */ +size_t +GNUNET_RECLAIM_presentation_serialize ( + const struct GNUNET_RECLAIM_Presentation *presentation, + char *result) +{ + struct Presentation *atts; + char *write_ptr; + + atts = (struct Presentation *) result; + atts->presentation_type = htons (presentation->type); + atts->credential_id = presentation->credential_id; + write_ptr = (char *) &atts[1]; + GNUNET_memcpy (write_ptr, presentation->data, presentation->data_size); + atts->data_size = htons (presentation->data_size); + + return sizeof(struct Presentation) + presentation->data_size; +} + + +/** + * Deserialize an presentation + * + * @param data the serialized presentation + * @param data_size the length of the serialized data + * + * @return a GNUNET_IDENTITY_PROVIDER_Attribute, must be free'd by caller + */ +struct GNUNET_RECLAIM_Presentation * +GNUNET_RECLAIM_presentation_deserialize (const char *data, size_t data_size) +{ + struct GNUNET_RECLAIM_Presentation *presentation; + struct Presentation *atts; + size_t data_len; + char *write_ptr; + + if (data_size < sizeof(struct Presentation)) + return NULL; + + atts = (struct Presentation *) data; + data_len = ntohs (atts->data_size); + if (data_size < sizeof(struct Presentation) + data_len) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Buffer too small to deserialize\n"); + return NULL; + } + presentation = GNUNET_malloc (sizeof(struct GNUNET_RECLAIM_Presentation) + + data_len); + presentation->type = ntohs (atts->presentation_type); + presentation->credential_id = atts->credential_id; + presentation->data_size = data_len; + + write_ptr = (char *) &presentation[1]; + GNUNET_memcpy (write_ptr, &atts[1], data_len); + presentation->data = write_ptr; + return presentation; +} + + +struct GNUNET_RECLAIM_AttributeList* +GNUNET_RECLAIM_presentation_get_attributes (const struct + GNUNET_RECLAIM_Presentation * + presentation) +{ + unsigned int i; + struct Plugin *plugin; + struct GNUNET_RECLAIM_AttributeList *ret; + init (); + for (i = 0; i < num_plugins; i++) + { + plugin = credential_plugins[i]; + if (NULL != + (ret = plugin->api->get_attributes_p (plugin->api->cls, + presentation))) + return ret; + } + return NULL; +} + + +char* +GNUNET_RECLAIM_presentation_get_issuer (const struct + GNUNET_RECLAIM_Presentation * + presentation) +{ + unsigned int i; + struct Plugin *plugin; + char *ret; + init (); + for (i = 0; i < num_plugins; i++) + { + plugin = credential_plugins[i]; + if (NULL != + (ret = plugin->api->get_issuer_p (plugin->api->cls, + presentation))) + return ret; + } + return NULL; +} + + +int +GNUNET_RECLAIM_presentation_get_expiration (const struct + GNUNET_RECLAIM_Presentation * + presentation, + struct GNUNET_TIME_Absolute*exp) +{ + unsigned int i; + struct Plugin *plugin; + init (); + for (i = 0; i < num_plugins; i++) + { + plugin = credential_plugins[i]; + if (GNUNET_OK != plugin->api->get_expiration_p (plugin->api->cls, + presentation, + exp)) + continue; + return GNUNET_OK; + } + return GNUNET_SYSERR; +} + +/** + * Create a presentation from a credential and a lift of (selected) + * attributes in the credential. + * FIXME not yet implemented + * + * @param cred the credential to use + * @param attrs the attributes to present from the credential + * @return the credential presentation presenting the attributes according + * to the presentation mechanism of the credential + * or NULL on error. + */ +int +GNUNET_RECLAIM_credential_get_presentation ( + const struct GNUNET_RECLAIM_Credential *cred, + const struct GNUNET_RECLAIM_AttributeList *attrs, + struct GNUNET_RECLAIM_Presentation **pres) +{ + unsigned int i; + struct Plugin *plugin; + init (); + for (i = 0; i < num_plugins; i++) + { + plugin = credential_plugins[i]; + if (GNUNET_OK != plugin->api->create_presentation (plugin->api->cls, + cred, + attrs, + pres)) + continue; + (*pres)->credential_id = cred->id; + return GNUNET_OK; + } + return GNUNET_SYSERR; +} + + + diff --git a/src/reclaim/reclaim_credential.h b/src/reclaim/reclaim_credential.h index cd3a9539a..7704ed968 100644 --- a/src/reclaim/reclaim_credential.h +++ b/src/reclaim/reclaim_credential.h @@ -61,4 +61,39 @@ struct Credential // followed by data_size Credential value data }; + +/** + * Serialized presentation claim + */ +struct Presentation +{ + /** + * Presentation type + */ + uint32_t presentation_type; + + /** + * Presentation flag + */ + uint32_t presentation_flag; + + /** + * Credential ID + */ + struct GNUNET_RECLAIM_Identifier credential_id; + + /** + * Name length + */ + uint32_t name_len; + + /** + * Data size + */ + uint32_t data_size; + + // followed by data_size Presentation value data +}; + + #endif -- cgit v1.2.3 From ef86866cee09c03bf426f1c84c15ecae0e5ba39d Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 23 Aug 2020 16:52:26 +0200 Subject: add function GNUNET_CURL_extend_headers() --- src/curl/curl.c | 27 ++++++++++++++++++++++++++- src/include/gnunet_curl_lib.h | 22 ++++++++++++++++------ src/include/gnunet_setu_service.h | 1 - 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/curl/curl.c b/src/curl/curl.c index eb9dd6a29..d89c97176 100644 --- a/src/curl/curl.c +++ b/src/curl/curl.c @@ -471,6 +471,30 @@ setup_job (CURL *eh, } +/** + * Add @a extra_headers to the HTTP headers for @a job. + * + * @param[in,out] job the job to modify + * @param extra_headers headers to append + */ +void +GNUNET_CURL_extend_headers (struct GNUNET_CURL_Job *job, + const struct curl_slist *extra_headers) +{ + struct curl_slist *all_headers = job->job_headers; + + for (const struct curl_slist *curr = extra_headers; + NULL != curr; + curr = curr->next) + { + GNUNET_assert (NULL != + (all_headers = curl_slist_append (all_headers, + curr->data))); + } + job->job_headers = all_headers; +} + + /** * Schedule a CURL request to be executed and call the given @a jcc * upon its completion. Note that the context will make use of the @@ -864,7 +888,8 @@ do_benchmark (CURLMsg *cmsg) curl -w "foo%{size_request} -XPOST --data "ABC" $URL the CURLINFO_REQUEST_SIZE should be the whole size of the request including headers and body. - */GNUNET_break (size_curl <= size_long); + */// + GNUNET_break (size_curl <= size_long); urd = get_url_benchmark_data (url, (unsigned int) response_code); urd->count++; diff --git a/src/include/gnunet_curl_lib.h b/src/include/gnunet_curl_lib.h index 9de58d608..f291d6b14 100644 --- a/src/include/gnunet_curl_lib.h +++ b/src/include/gnunet_curl_lib.h @@ -275,9 +275,8 @@ GNUNET_CURL_set_userpass (struct GNUNET_CURL_Context *ctx, /** - * Force use of the provided TLS client certificate - * for client authentication for all operations performed - * with @a ctx. + * Force use of the provided TLS client certificate for client authentication + * for all operations performed with @a ctx. * * Note that if the provided information is incorrect, * the earliest operation that could fail is @@ -298,9 +297,9 @@ GNUNET_CURL_set_tlscert (struct GNUNET_CURL_Context *ctx, /** - * Schedule a CURL request to be executed and call the given @a jcc - * upon its completion. Note that the context will make use of the - * CURLOPT_PRIVATE facility of the CURL @a eh. + * Schedule a CURL request to be executed and call the given @a jcc upon its + * completion. Note that the context will make use of the CURLOPT_PRIVATE + * facility of the CURL @a eh. * * This function modifies the CURL handle to add the * "Content-Type: application/json" header if @a add_json is set. @@ -344,6 +343,17 @@ GNUNET_CURL_job_add_raw (struct GNUNET_CURL_Context *ctx, void *jcc_cls); +/** + * Add @a extra_headers to the HTTP headers for @a job. + * + * @param[in,out] job the job to modify + * @param extra_headers headers to append + */ +void +GNUNET_CURL_extend_headers (struct GNUNET_CURL_Job *job, + const struct curl_slist *extra_headers); + + /** * Cancel a job. Must only be called before the job completion * callback is called for the respective job. diff --git a/src/include/gnunet_setu_service.h b/src/include/gnunet_setu_service.h index 459a6156b..634c5c40b 100644 --- a/src/include/gnunet_setu_service.h +++ b/src/include/gnunet_setu_service.h @@ -162,7 +162,6 @@ enum GNUNET_SETU_OptionType /** * Notify client also if we are sending a value to the other peer. - * FIXME: not implemented! */ GNUNET_SETU_OPTION_SYMMETRIC = 8 }; -- cgit v1.2.3 From f520e9044a69290668bbd9f956ebe3750f4d27d0 Mon Sep 17 00:00:00 2001 From: Jonathan Buchanan Date: Mon, 24 Aug 2020 20:54:18 -0400 Subject: header for uri parser --- src/include/Makefile.am | 1 + src/include/gnunet_uri_lib.h | 96 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 src/include/gnunet_uri_lib.h diff --git a/src/include/Makefile.am b/src/include/Makefile.am index d2c254ae6..e542038d3 100644 --- a/src/include/Makefile.am +++ b/src/include/Makefile.am @@ -119,5 +119,6 @@ gnunetinclude_HEADERS = \ gnunet_transport_monitor_service.h \ gnunet_transport_plugin.h \ gnunet_tun_lib.h \ + gnunet_uri_lib.h \ gnunet_util_lib.h \ gnunet_vpn_service.h diff --git a/src/include/gnunet_uri_lib.h b/src/include/gnunet_uri_lib.h new file mode 100644 index 000000000..86c9c54f4 --- /dev/null +++ b/src/include/gnunet_uri_lib.h @@ -0,0 +1,96 @@ +/* + This file is part of GNUnet + Copyright (C) 2020 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + 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 + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file include/gnunet_uri_lib.h + * @brief generic parser for URIs + * @author Jonathan Buchanan + */ + +#ifndef GNUNET_URI_LIB_H +#define GNUNET_URI_LIB_H + +/** + * A Universal Resource Identifier (URI). + */ +struct GNUNET_Uri +{ + /** + * The scheme of the uri. + */ + char *scheme; + + + /** + * The authority of the uri. If not present in the uri, NULL. + */ + char *authority; + + + /** + * The list of path segments in the URI. Note that if the path ends with a + * '/', then this array will end with an empty string to indicate the empty + * segment following the '/'. + */ + char **path_segments; + + + /** + * The length of @e path_segments. + */ + unsigned int path_segments_count; + + + /** + * The query of the uri. If not present in the uri, NULL. + */ + const char *query; + + + /** + * The fragment of the uri. If not present in the uri, NULL. + */ + char *fragment; +}; + + +/** + * Parse a URI from a string into an internal representation. + * + * @param uri string to parse + * @param emsg where to store the parser error message (if any) + * @return handle to the internal representation of the URI, or NULL on error + */ +struct GNUNET_Uri * +GNUNET_uri_parse (const char *uri, + char **emsg); + + +/** + * Free URI. + * + * @param uri uri to free + */ +void +GNUNET_uri_destroy (struct GNUNET_Uri *uri); + + +#endif /* GNUNET_URI_LIB_H */ + +/* end of include/gnunet_uri_lib.h */ -- cgit v1.2.3 From e36180a17c8da8d3639f42924ce56c11fec1fddd Mon Sep 17 00:00:00 2001 From: Jonathan Buchanan Date: Mon, 24 Aug 2020 21:16:24 -0400 Subject: remove const from query string in GNUNET_uri --- src/include/gnunet_uri_lib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/gnunet_uri_lib.h b/src/include/gnunet_uri_lib.h index 86c9c54f4..48db0ac85 100644 --- a/src/include/gnunet_uri_lib.h +++ b/src/include/gnunet_uri_lib.h @@ -60,7 +60,7 @@ struct GNUNET_Uri /** * The query of the uri. If not present in the uri, NULL. */ - const char *query; + char *query; /** -- cgit v1.2.3 From 49d74db2e9bd8418f15b560e36e1f27661f65361 Mon Sep 17 00:00:00 2001 From: Jonathan Buchanan Date: Wed, 26 Aug 2020 14:49:02 -0400 Subject: use (and "GNUnet-ify") libyuarel as a basepoint for uri parsing --- src/include/gnunet_uri_lib.h | 170 +++++---- src/util/.gitignore | 3 +- src/util/Makefile.am | 7 + src/util/test_uri.c | 837 +++++++++++++++++++++++++++++++++++++++++++ src/util/uri.c | 344 ++++++++++++++++++ 5 files changed, 1288 insertions(+), 73 deletions(-) create mode 100644 src/util/test_uri.c create mode 100644 src/util/uri.c diff --git a/src/include/gnunet_uri_lib.h b/src/include/gnunet_uri_lib.h index 48db0ac85..e5f144591 100644 --- a/src/include/gnunet_uri_lib.h +++ b/src/include/gnunet_uri_lib.h @@ -1,96 +1,122 @@ -/* - This file is part of GNUnet - Copyright (C) 2020 GNUnet e.V. - - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - 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 - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - SPDX-License-Identifier: AGPL3.0-or-later - */ /** - * @file include/gnunet_uri_lib.h - * @brief generic parser for URIs - * @author Jonathan Buchanan + * Copyright (C) 2016 Jack Engqvist Johansson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ - #ifndef GNUNET_URI_LIB_H #define GNUNET_URI_LIB_H + /** - * A Universal Resource Identifier (URI). + * The struct where the parsed values will be stored: + * + * scheme ":" [ "//" ] [ username ":" password "@" ] host [ ":" port ] [ "/" ] [ path ] [ "?" query ] + * + * Note: to make sure that no strings are copied, the first slash "/" in the + * path will be used to null terminate the hostname if no port is supplied. */ -struct GNUNET_Uri -{ - /** - * The scheme of the uri. - */ - char *scheme; - - - /** - * The authority of the uri. If not present in the uri, NULL. - */ - char *authority; - - - /** - * The list of path segments in the URI. Note that if the path ends with a - * '/', then this array will end with an empty string to indicate the empty - * segment following the '/'. - */ - char **path_segments; - - - /** - * The length of @e path_segments. - */ - unsigned int path_segments_count; +struct GNUNET_uri { + char *scheme; /* scheme, without ":" and "//" */ + char *username; /* username, default: NULL */ + char *password; /* password, default: NULL */ + char *host; /* hostname or IP address */ + int port; /* port, default: 0 */ + char *path; /* path, without leading "/", default: NULL */ + char *query; /* query, default: NULL */ + char *fragment; /* fragment, default: NULL */ +}; - /** - * The query of the uri. If not present in the uri, NULL. - */ - char *query; +/* A struct to hold the query string parameter values. */ +struct GNUNET_uri_param { + char *key; + char *val; +}; - /** - * The fragment of the uri. If not present in the uri, NULL. - */ - char *fragment; -}; +/** + * Parse a URL to a struct. + * + * The URL string should be in one of the following formats: + * + * Absolute URL: + * scheme ":" [ "//" ] [ username ":" password "@" ] host [ ":" port ] [ "/" ] [ path ] [ "?" query ] [ "#" fragment ] + * + * Relative URL: + * path [ "?" query ] [ "#" fragment ] + * + * The following parts will be parsed to the corresponding struct member. + * + * *url: a pointer to the struct where to store the parsed values. + * *url_str: a pointer to the url to be parsed (null terminated). The string + * will be modified. + * + * Returns 0 on success, otherwise -1. + */ +int +GNUNET_uri_parse (struct GNUNET_uri *url, + char *url_str); /** - * Parse a URI from a string into an internal representation. + * Split a path into several strings. + * + * No data is copied, the slashed are used as null terminators and then + * pointers to each path part will be stored in **parts. Double slashes will be + * treated as one. + * + * *path: the path to split. The string will be modified. + * **parts: a pointer to an array of (char *) where to store the result. + * max_parts: max number of parts to parse. * - * @param uri string to parse - * @param emsg where to store the parser error message (if any) - * @return handle to the internal representation of the URI, or NULL on error + * Returns the number of parsed items. -1 on error. */ -struct GNUNET_Uri * -GNUNET_uri_parse (const char *uri, - char **emsg); +int +GNUNET_uri_split_path (char *path, + char **parts, + int max_parts); /** - * Free URI. + * Parse a query string into a key/value struct. * - * @param uri uri to free + * The query string should be a null terminated string of parameters separated by + * a delimiter. Each parameter are checked for the equal sign character. If it + * appears in the parameter, it will be used as a null terminator and the part + * that comes after it will be the value of the parameter. + * + * No data are copied, the equal sign and delimiters are used as null + * terminators and then pointers to each parameter key and value will be stored + * in the yuarel_param struct. + * + * *query: the query string to parse. The string will be modified. + * delimiter: the character that separates the key/value pairs from eachother. + * *params: an array of (struct yuarel_param) where to store the result. + * max_values: max number of parameters to parse. + * + * Returns the number of parsed items. -1 on error. */ -void -GNUNET_uri_destroy (struct GNUNET_Uri *uri); +int +GNUNET_uri_parse_query (char *query, + char delimiter, + struct GNUNET_uri_param *params, + int max_params); #endif /* GNUNET_URI_LIB_H */ - -/* end of include/gnunet_uri_lib.h */ diff --git a/src/util/.gitignore b/src/util/.gitignore index 05f187869..8556ee7b8 100644 --- a/src/util/.gitignore +++ b/src/util/.gitignore @@ -71,9 +71,10 @@ perf_crypto_hash perf_crypto_symmetric perf_crypto_rsa perf_crypto_ecc_dlog -test_hexcoder +test_hexcoder test_regex test_tun +test_uri gnunet-timeout python27_location perf_malloc diff --git a/src/util/Makefile.am b/src/util/Makefile.am index 83b3b9c3d..c5059bbb1 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -96,6 +96,7 @@ libgnunetutil_la_SOURCES = \ strings.c \ time.c \ tun.c \ + uri.c \ speedup.c speedup.h \ proc_compat.c @@ -302,6 +303,7 @@ check_PROGRAMS = \ test_speedup \ test_time \ test_tun \ + test_uri \ $(BENCHMARKS) \ test_os_start_process \ test_common_logging_runtime_loglevels @@ -573,6 +575,11 @@ test_speedup_SOURCES = \ test_speedup_LDADD = \ libgnunetutil.la +test_uri_SOURCES = \ + test_uri.c +test_uri_LDADD = \ + libgnunetutil.la + perf_crypto_hash_SOURCES = \ perf_crypto_hash.c perf_crypto_hash_LDADD = \ diff --git a/src/util/test_uri.c b/src/util/test_uri.c new file mode 100644 index 000000000..f9e38383a --- /dev/null +++ b/src/util/test_uri.c @@ -0,0 +1,837 @@ +#include +#include +#include +#include "gnunet_uri_lib.h" + +#define KNRM "\x1B[0m" +#define KBLU "\x1B[34m" +#define KGRN "\x1B[32m" +#define KERR "\x1B[5;31;50m" + +/* macro to print out the header for a new group of tests */ +#define mu_group(name) printf ("%s • %s%s\n", KBLU, name, KNRM) + +/* macro for asserting a statement */ +#define mu_assert(message, test) do { \ + if (!(test)) { \ + printf ("\t%s× %s%s\n", KERR, message, KNRM); \ + return message; \ + } \ + printf ("\t%s• %s%s\n", KGRN, message, KNRM); \ + } while (0) + +/* macro for asserting a statement without printing it unless it is a failure */ +#define mu_silent_assert(message, test) do { \ + if (!(test)) { \ + printf ("\t\t%s× %s%s\n", KERR, message, KNRM); \ + return message; \ + } \ + } while (0) + +/* run a test function and return result */ +#define mu_run_test(test) do { \ + char *message = test (); tests_run++; \ + if (message) { return message; } \ + } while (0) + + +int tests_run; + +static int +strcmp_wrap (const char *str, + const char *str2) +{ + if (NULL == str && NULL == str2) { + return 0; + } + if (NULL == str) { + return 1; + } + if (NULL == str2) { + return -1; + } + + return strcmp (str, str2); +} + +#define assert_struct(as_url, \ + as_scheme, \ + as_user, \ + as_pass, \ + as_host, \ + as_port, \ + as_path, \ + as_query, \ + as_fragment) \ + mu_silent_assert ("should set the scheme attribute correctly", \ + 0 == strcmp_wrap (as_url.scheme, as_scheme)); \ + mu_silent_assert ("should set the username attribute correctly", \ + 0 == strcmp_wrap (as_url.username, as_user)); \ + mu_silent_assert ("should set the password attribute correctly", \ + 0 == strcmp_wrap (as_url.password, as_pass)); \ + mu_silent_assert ("should set the host attribute correctly", \ + 0 == strcmp_wrap (as_url.host, as_host)); \ + mu_silent_assert ("should set the port attribute correctly", \ + as_port == as_url.port); \ + mu_silent_assert ("should set the path attribute correctly", \ + 0 == strcmp_wrap (as_url.path, as_path)); \ + mu_silent_assert ("should set the query attribute correctly", \ + 0 == strcmp_wrap (as_url.query, as_query)); \ + mu_silent_assert ("should set the fragment attribute correctly", \ + 0 == strcmp_wrap (as_url.fragment, as_fragment)); + +static char * +test_parse_http_url_ok (void) +{ + int rc; + struct GNUNET_uri url; + char *url_string; + + /* Minimal URL */ + url_string = strdup ("http://example.com"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("minimal HTTP URL", -1 != rc); + assert_struct (url, + "http", + NULL, + NULL, + "example.com", + 0, + NULL, + NULL, + NULL); + free (url_string); + + /* With path (/) */ + url_string = strdup ("http://example.com/"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("with path ('/')", -1 != rc); + assert_struct (url, + "http", + NULL, + NULL, + "example.com", + 0, + "", + NULL, + NULL); + free (url_string); + + /* With path */ + url_string = strdup ("http://example.com/path"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("with path ('/path')", -1 != rc); + assert_struct (url, + "http", + NULL, + NULL, + "example.com", + 0, + "path", + NULL, + NULL); + free (url_string); + + /* With port */ + url_string = strdup ("http://example.com:80"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("with port only", + -1 != rc); + assert_struct (url, + "http", + NULL, + NULL, + "example.com", + 80, + NULL, + NULL, + NULL); + free (url_string); + + /* With query */ + url_string = strdup ("http://example.com?query=only"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("with query only", + -1 != rc); + assert_struct (url, + "http", + NULL, + NULL, + "example.com", + 0, + NULL, + "query=only", + NULL); + free (url_string); + + /* With fragment */ + url_string = strdup ("http://example.com#frag=f1"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("with fragment only", + -1 != rc); + assert_struct (url, + "http", + NULL, + NULL, + "example.com", + 0, + NULL, + NULL, + "frag=f1"); + free (url_string); + + /* With credentials */ + url_string = strdup ("http://u:p@example.com"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("with credentials only", + -1 != rc); + assert_struct (url, + "http", + "u", + "p", + "example.com", + 0, + NULL, + NULL, + NULL); + free (url_string); + + /* With port and path */ + url_string = strdup ("http://example.com:8080/port/and/path"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("with port and path", + -1 != rc); + assert_struct (url, + "http", + NULL, + NULL, + "example.com", + 8080, + "port/and/path", + NULL, + NULL); + free (url_string); + + /* With port and query */ + url_string = strdup ("http://example.com:8080?query=portANDquery"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("with port and query", + -1 != rc); + assert_struct (url, + "http", + NULL, + NULL, + "example.com", + 8080, + NULL, + "query=portANDquery", + NULL); + free (url_string); + + /* With port and fragment */ + url_string = strdup ("http://example.com:8080#f1"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("with port and fragment", + -1 != rc); + assert_struct (url, + "http", + NULL, + NULL, + "example.com", + 8080, + NULL, + NULL, + "f1"); + free (url_string); + + /* With port and credentials */ + url_string = strdup ("http://u:p@example.com:8080"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("with port and credentials", + -1 != rc); + assert_struct (url, + "http", + "u", + "p", + "example.com", + 8080, + NULL, + NULL, + NULL); + free (url_string); + + /* With path and query */ + url_string = strdup ("http://example.com/path/and/query?q=yes"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("with path and query", + -1 != rc); + assert_struct (url, + "http", + NULL, + NULL, + "example.com", + 0, + "path/and/query", + "q=yes", + NULL); + free (url_string); + + /* With path and fragment */ + url_string = strdup ("http://example.com/path/and#fragment"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("with path and fragment", + -1 != rc); + assert_struct (url, + "http", + NULL, + NULL, + "example.com", + 0, + "path/and", + NULL, + "fragment"); + free (url_string); + + /* With query and fragment */ + url_string = strdup ("http://example.com?q=yes#f1"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("with query and fragment", + -1 != rc); + assert_struct (url, + "http", + NULL, + NULL, + "example.com", + 0, + NULL, + "q=yes", + "f1"); + free (url_string); + + /* With query and credentials */ + url_string = strdup ("http://u:p@example.com?q=yes"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("with query and credentials", + -1 != rc); + assert_struct (url, + "http", + "u", + "p", + "example.com", + 0, + NULL, + "q=yes", + NULL); + free (url_string); + + /* With empty credentials */ + url_string = strdup ("http://:@example.com"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("with empty credentials", + -1 != rc); + assert_struct (url, + "http", + "", + "", + "example.com", + 0, + NULL, + NULL, + NULL); + free (url_string); + + /* With empty credentials and port */ + url_string = strdup ("http://:@example.com:89"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("with empty credentials and port", + -1 != rc); + assert_struct (url, + "http", + "", + "", + "example.com", + 89, + NULL, + NULL, + NULL); + free (url_string); + + /* Full URL */ + url_string = strdup ("https://jack:password@localhost:8989/path/to/test?query=yes&q=jack#fragment1"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("with port, path and query", + -1 != rc); + assert_struct (url, + "https", + "jack", + "password", + "localhost", + 8989, + "path/to/test", + "query=yes&q=jack", + "fragment1"); + free (url_string); + + return NULL; +} + +static char * +test_parse_http_rel_url_ok (void) +{ + int rc; + struct GNUNET_uri url; + char *url_string; + + /* Minimal relative URL */ + url_string = strdup ("/"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("minimal relative URL", + -1 != rc); + assert_struct (url, + NULL, + NULL, + NULL, + NULL, + 0, + "", + NULL, + NULL); + free (url_string); + + /* Path only */ + url_string = strdup ("/hejsan"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("path only", + -1 != rc); + assert_struct (url, + NULL, + NULL, + NULL, + NULL, + 0, + "hejsan", + NULL, + NULL); + free (url_string); + + /* Path and query */ + url_string = strdup ("/hejsan?q=yes"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("path only", + -1 != rc); + assert_struct (url, + NULL, + NULL, + NULL, + NULL, + 0, + "hejsan", + "q=yes", + NULL); + free (url_string); + + /* Path and fragment */ + url_string = strdup ("/hejsan#fragment"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("path and fragment", + -1 != rc); + assert_struct (url, + NULL, + NULL, + NULL, + NULL, + 0, + "hejsan", + NULL, + "fragment"); + free (url_string); + + /* Path, query and fragment */ + url_string = strdup ("/?q=yes&q2=no#fragment"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("path, query and fragment", + -1 != rc); + assert_struct (url, + NULL, + NULL, + NULL, + NULL, + 0, + "", + "q=yes&q2=no", + "fragment"); + free (url_string); + + return NULL; +} + +static char * +test_parse_url_fail (void) +{ + int rc; + struct GNUNET_uri url; + char *url_string; + + /* Empty */ + url_string = strdup (""); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("empty string should return -1", + -1 == rc); + free (url_string); + + /* Scheme only */ + url_string = strdup ("rtsp://"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("scheme only should return -1", + -1 == rc); + free (url_string); + + /* Hostname only */ + url_string = strdup ("hostname"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("hostname only should return -1", + -1 == rc); + free (url_string); + + /* Query only */ + url_string = strdup ("?query=only"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("query only should return -1", + -1 == rc); + free (url_string); + + /* Missing scheme */ + url_string = strdup ("://"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("missing scheme should return -1", + -1 == rc); + free (url_string); + + /* Missing hostname */ + url_string = strdup ("rtsp://:8910/path"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("missing hostname should return -1", + -1 == rc); + free (url_string); + + /* Missing credentials */ + url_string = strdup ("rtsp://@hostname:8910/path"); + rc = GNUNET_uri_parse (&url, + url_string); + mu_assert ("missing credentials should return -1", + -1 == rc); + free (url_string); + + return NULL; +} + +static char * +test_split_path_ok (void) +{ + int rc; + char *path; + char *parts[10]; + + /* Simple path */ + path = strdup ("/this/is/a/path"); + rc = GNUNET_uri_split_path (path, + parts, + 10); + mu_assert ("should be able to parse a regular path", + 4 == rc); + mu_silent_assert ("first part should be 'this'", + 0 == strcmp ("this", parts[0])); + mu_silent_assert ("second part should be 'is'", + 0 == strcmp ("is", parts[1])); + mu_silent_assert ("third part should be 'a'", + 0 == strcmp ("a", parts[2])); + mu_silent_assert ("fourth part should be 'path'", + 0 == strcmp ("path", parts[3])); + free (path); + + /* Relative path */ + path = strdup ("this/is/a/path"); + rc = GNUNET_uri_split_path (path, + parts, + 10); + mu_assert ("should be able to parse a relative path", + 4 == rc); + mu_silent_assert ("first part should be 'this'", + 0 == strcmp ("this", parts[0])); + mu_silent_assert ("second part should be 'is'", + 0 == strcmp ("is", parts[1])); + mu_silent_assert ("third part should be 'a'", + 0 == strcmp ("a", parts[2])); + mu_silent_assert ("fourth part should be 'path'", + 0 == strcmp ("path", parts[3])); + free (path); + + /* Path with empty parts */ + path = strdup ("//this//is/a/path/"); + rc = GNUNET_uri_split_path (path, + parts, + 10); + mu_assert ("should treat multiple slashes as one", + 4 == rc); + mu_silent_assert ("first part should be 'this'", + 0 == strcmp("this", parts[0])); + mu_silent_assert ("second part should be 'is'", + 0 == strcmp("is", parts[1])); + mu_silent_assert ("third part should be 'a'", + 0 == strcmp("a", parts[2])); + mu_silent_assert ("fourth part should be 'path'", + 0 == strcmp("path", parts[3])); + free (path); + + /* Just one level */ + path = strdup("/one_level"); + rc = GNUNET_uri_split_path(path, parts, 10); + mu_assert("should be able to parse a path with one level", 1 == rc); + mu_silent_assert("first part should be 'this'", 0 == strcmp("one_level", parts[0])); + free(path); + + return NULL; +} + +static char * +test_parse_query_ok (void) +{ + int rc; + char *q; + struct GNUNET_uri_param params[10]; + + /* One param query */ + q = strdup ("q=yes"); + rc = GNUNET_uri_parse_query (q, + '&', + params, + 10); + mu_assert ("single parameter with value", + 1 == rc); + mu_silent_assert ("first param key should be 'q'", + 0 == strcmp ("q", params[0].key)); + mu_silent_assert ("first param val should be 'yes'", + 0 == strcmp ("yes", params[0].val)); + free (q); + + /* One param query without value */ + q = strdup ("q"); + rc = GNUNET_uri_parse_query (q, + '&', + params, + 10); + mu_assert ("single parameter without value", + 1 == rc); + mu_silent_assert ("first param key should be 'q'", + 0 == strcmp ("q", params[0].key)); + mu_silent_assert ("first param val should be NULL", + NULL == params[0].val); + free (q); + + /* Two param query */ + q = strdup ("query=yes&a1=hello"); + rc = GNUNET_uri_parse_query (q, + '&', + params, + 10); + mu_assert ("multiple params with value", + 2 == rc); + mu_silent_assert ("first param key should be 'query'", + 0 == strcmp ("query", params[0].key)); + mu_silent_assert ("first param val should be 'yes'", + 0 == strcmp ("yes", params[0].val)); + mu_silent_assert ("second param key should be 'a1'", + 0 == strcmp ("a1", params[1].key)); + mu_silent_assert ("second param val should be 'hello'", + 0 == strcmp ("hello", params[1].val)); + free (q); + + /* Two param query, one without value */ + q = strdup ("query=yes&forceHttps"); + rc = GNUNET_uri_parse_query (q, + '&', + params, + 10); + mu_assert ("multiple params one without value", + 2 == rc); + mu_silent_assert ("first param key should be 'query'", + 0 == strcmp ("query", params[0].key)); + mu_silent_assert ("first param val should be 'yes'", + 0 == strcmp ("yes", params[0].val)); + mu_silent_assert ("second param key should be 'forceHttps'", + 0 == strcmp ("forceHttps", params[1].key)); + mu_silent_assert ("second param val should be NULL", + NULL == params[1].val); + free (q); + + /* Three param query, all without value */ + q = strdup ("query&forceHttps&log"); + rc = GNUNET_uri_parse_query (q, + '&', + params, + 10); + mu_assert ("multiple params all without value", + 3 == rc); + mu_silent_assert ("first param key should be 'query'", + 0 == strcmp ("query", params[0].key)); + mu_silent_assert ("first param val should be NULL", + NULL == params[0].val); + mu_silent_assert ("second param key should be 'forceHttps'", + 0 == strcmp ("forceHttps", params[1].key)); + mu_silent_assert ("second param val should be NULL", + NULL == params[1].val); + mu_silent_assert ("third param key should be 'log'", + 0 == strcmp ("log", params[2].key)); + mu_silent_assert ("third param val should be NULL", + NULL == params[2].val); + free (q); + + /* Param with empty value */ + q = strdup ("param=&query=no"); + rc = GNUNET_uri_parse_query (q, + '&', + params, + 10); + mu_assert ("param with empty value", + 2 == rc); + mu_silent_assert ("first param key should be 'param'", + 0 == strcmp ("param", params[0].key)); + mu_silent_assert ("first param val should be ''", + 0 == strcmp ("", params[0].val)); + mu_silent_assert ("second param key should be 'query'", + 0 == strcmp ("query", params[1].key)); + mu_silent_assert ("second param val should be 'no'", + 0 == strcmp ("no", params[1].val)); + free (q); + + /* Double delimiter */ + q = strdup ("param=jack&&query=no"); + rc = GNUNET_uri_parse_query (q, + '&', + params, + 10); + mu_assert ("double delimiter", + 3 == rc); + mu_silent_assert ("first param key should be 'param'", + 0 == strcmp ("param", params[0].key)); + mu_silent_assert ("first param val should be 'jack'", + 0 == strcmp ("jack", params[0].val)); + mu_silent_assert ("second param key should be ''", + 0 == strcmp ("", params[1].key)); + mu_silent_assert ("second param val should be NULL", + NULL == params[1].val); + mu_silent_assert ("third param key should be 'query'", + 0 == strcmp ("query", params[2].key)); + mu_silent_assert ("third param val should be 'no'", + 0 == strcmp ("no", params[2].val)); + free (q); + + /* Delimiter in beginning */ + q = strdup ("¶m=jack&query=no"); + rc = GNUNET_uri_parse_query (q, + '&', + params, + 10); + mu_assert ("delimiter in beginning", + 3 == rc); + mu_silent_assert ("first param key should be ''", + 0 == strcmp ("", params[0].key)); + mu_silent_assert ("first param val should be NULL", + NULL == params[0].val); + mu_silent_assert ("second param key should be 'param'", + 0 == strcmp ("param", params[1].key)); + mu_silent_assert ("second param val should be 'jack'", + 0 == strcmp ("jack", params[1].val)); + mu_silent_assert ("third param key should be 'query'", + 0 == strcmp ("query", params[2].key)); + mu_silent_assert ("third param val should be 'no'", + 0 == strcmp ("no", params[2].val)); + free (q); + + /* Delimiter at the end */ + q = strdup ("param=jack&query=no&"); + rc = GNUNET_uri_parse_query (q, + '&', + params, + 10); + mu_assert ("delimiter at the end", + 3 == rc); + mu_silent_assert ("first param key should be 'param'", + 0 == strcmp ("param", params[0].key)); + mu_silent_assert ("first param val should be 'jack'", + 0 == strcmp ("jack", params[0].val)); + mu_silent_assert ("second param key should be 'query'", + 0 == strcmp ("query", params[1].key)); + mu_silent_assert ("second param val should be 'no'", + 0 == strcmp ("no", params[1].val)); + mu_silent_assert ("third param key should be ''", + 0 == strcmp ("", params[2].key)); + mu_silent_assert ("third param val should be NULL", + NULL == params[2].val); + free (q); + + return NULL; +} + +static char * +all_tests (void) +{ + mu_group ("GNUNET_uri_parse () with an HTTP URL"); + mu_run_test (test_parse_http_url_ok); + + mu_group ("GNUNET_uri_parse () with an relative URL"); + mu_run_test (test_parse_http_rel_url_ok); + + mu_group ("GNUNET_uri_parse () with faulty values"); + mu_run_test (test_parse_url_fail); + + mu_group ("GNUNET_uri_split_path ()"); + mu_run_test (test_split_path_ok); + + mu_group ("GNUNET_uri_parse_query ()"); + mu_run_test (test_parse_query_ok); + + return NULL; +} + +int +main (void) +{ + char *result; + + result = all_tests (); + if (result != NULL) { + exit (EXIT_FAILURE); + } + + exit (EXIT_SUCCESS); +} diff --git a/src/util/uri.c b/src/util/uri.c new file mode 100644 index 000000000..ba5c0f716 --- /dev/null +++ b/src/util/uri.c @@ -0,0 +1,344 @@ +/** + * Copyright (C) 2016,2017 Jack Engqvist Johansson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include +#include +#include +#include "gnunet_uri_lib.h" + + +/** + * Parse a non null terminated string into an integer. + * + * str: the string containing the number. + * len: Number of characters to parse. + */ +static inline int +natoi (const char *str, + size_t len) +{ + int i, r = 0; + for (i = 0; i < len; i++) { + r *= 10; + r += str[i] - '0'; + } + + return r; +} + + +/** + * Check if a URL is relative (no scheme and hostname). + * + * url: the string containing the URL to check. + * + * Returns 1 if relative, otherwise 0. + */ +static inline int +is_relative (const char *url) +{ + return (*url == '/') ? 1 : 0; +} + + +/** + * Parse the scheme of a URL by inserting a null terminator after the scheme. + * + * str: the string containing the URL to parse. Will be modified. + * + * Returns a pointer to the hostname on success, otherwise NULL. + */ +static inline char * +parse_scheme (char *str) +{ + char *s; + + /* If not found or first in string, return error */ + s = strchr (str, ':'); + if (s == NULL || s == str) { + return NULL; + } + + /* If not followed by two slashes, return error */ + if (s[1] == '\0' || s[1] != '/' || s[2] == '\0' || s[2] != '/') { + return NULL; + } + + *s = '\0'; // Replace ':' with NULL + + return s + 3; +} + + +/** + * Find a character in a string, replace it with '\0' and return the next + * character in the string. + * + * str: the string to search in. + * find: the character to search for. + * + * Returns a pointer to the character after the one to search for. If not + * found, NULL is returned. + */ +static inline char * +find_and_terminate (char *str, + char find) +{ + str = strchr(str, find); + if (NULL == str) { + return NULL; + } + + *str = '\0'; + return str + 1; +} + + +/* Yes, the following functions could be implemented as preprocessor macros + instead of inline functions, but I think that this approach will be more + clean in this case. */ +static inline char * +find_fragment (char *str) +{ + return find_and_terminate (str, '#'); +} + + +static inline char * +find_query (char *str) +{ + return find_and_terminate (str, '?'); +} + + +static inline char * +find_path (char *str) +{ + return find_and_terminate (str, '/'); +} + + +/** + * Parse a URL to a struct. + * + * The URL string should be in one of the following formats: + * + * Absolute URL: + * scheme ":" [ "//" ] [ username ":" password "@" ] host [ ":" port ] [ "/" ] [ path ] [ "?" query ] [ "#" fragment ] + * + * Relative URL: + * path [ "?" query ] [ "#" fragment ] + * + * The following parts will be parsed to the corresponding struct member. + * + * *url: a pointer to the struct where to store the parsed values. + * *url_str: a pointer to the url to be parsed (null terminated). The string + * will be modified. + * + * Returns 0 on success, otherwise -1. + */ +int +GNUNET_uri_parse (struct GNUNET_uri *url, + char *u) +{ + if (NULL == url || NULL == u) { + return -1; + } + + memset(url, 0, sizeof (struct GNUNET_uri)); + + /* (Fragment) */ + url->fragment = find_fragment (u); + + /* (Query) */ + url->query = find_query (u); + + /* Relative URL? Parse scheme and hostname */ + if (!is_relative (u)) { + /* Scheme */ + url->scheme = u; + u = parse_scheme (u); + if (u == NULL) { + return -1; + } + + /* Host */ + if ('\0' == *u) { + return -1; + } + url->host = u; + + /* (Path) */ + url->path = find_path (u); + + /* (Credentials) */ + u = strchr (url->host, '@'); + if (NULL != u) { + /* Missing credentials? */ + if (u == url->host) { + return -1; + } + + url->username = url->host; + url->host = u + 1; + *u = '\0'; + + u = strchr (url->username, ':'); + if (NULL == u) { + return -1; + } + + url->password = u + 1; + *u = '\0'; + } + + /* Missing hostname? */ + if ('\0' == *url->host) { + return -1; + } + + /* (Port) */ + u = strchr (url->host, ':'); + if (NULL != u && (NULL == url->path || u < url->path)) { + *(u++) = '\0'; + if ('\0' == *u) { + return -1; + } + + if (url->path) { + url->port = natoi (u, url->path - u - 1); + } else { + url->port = atoi (u); + } + } + + /* Missing hostname? */ + if ('\0' == *url->host) { + return -1; + } + } else { + /* (Path) */ + url->path = find_path (u); + } + + return 0; +} + + +/** + * Split a path into several strings. + * + * No data is copied, the slashed are used as null terminators and then + * pointers to each path part will be stored in **parts. Double slashes will be + * treated as one. + * + * *path: the path to split. The string will be modified. + * **parts: a pointer to an array of (char *) where to store the result. + * max_parts: max number of parts to parse. + * + * Returns the number of parsed items. -1 on error. + */ +int +GNUNET_uri_split_path (char *path, + char **parts, + int max_parts) +{ + int i = 0; + + if (NULL == path || '\0' == *path) { + return -1; + } + + do { + /* Forward to after slashes */ + while (*path == '/') path++; + + if ('\0' == *path) { + break; + } + + parts[i++] = path; + + path = strchr (path, '/'); + if (NULL == path) { + break; + } + + *(path++) = '\0'; + } while (i < max_parts); + + return i; +} + + +/** + * Parse a query string into a key/value struct. + * + * The query string should be a null terminated string of parameters separated by + * a delimiter. Each parameter are checked for the equal sign character. If it + * appears in the parameter, it will be used as a null terminator and the part + * that comes after it will be the value of the parameter. + * + * No data are copied, the equal sign and delimiters are used as null + * terminators and then pointers to each parameter key and value will be stored + * in the yuarel_param struct. + * + * *query: the query string to parse. The string will be modified. + * delimiter: the character that separates the key/value pairs from eachother. + * *params: an array of (struct yuarel_param) where to store the result. + * max_values: max number of parameters to parse. + * + * Returns the number of parsed items. -1 on error. + */ +int +GNUNET_uri_parse_query (char *query, + char delimiter, + struct GNUNET_uri_param *params, + int max_params) +{ + int i = 0; + + if (NULL == query || '\0' == *query) { + return -1; + } + + params[i++].key = query; + while (i < max_params && NULL != (query = strchr (query, delimiter))) { + *query = '\0'; + params[i].key = ++query; + params[i].val = NULL; + + /* Go back and split previous param */ + if (i > 0) { + if ((params[i - 1].val = strchr (params[i - 1].key, '=')) != NULL) { + *(params[i - 1].val)++ = '\0'; + } + } + i++; + } + + /* Go back and split last param */ + if ((params[i - 1].val = strchr (params[i - 1].key, '=')) != NULL) { + *(params[i - 1].val)++ = '\0'; + } + + return i; +} -- cgit v1.2.3 From a3766e94489b0e02bcbf8ffb6695a3d29f4ca7ca Mon Sep 17 00:00:00 2001 From: Jonathan Buchanan Date: Thu, 27 Aug 2020 16:02:17 -0400 Subject: change uri structs to match conventions --- src/include/gnunet_uri_lib.h | 8 ++++---- src/util/test_uri.c | 8 ++++---- src/util/uri.c | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/include/gnunet_uri_lib.h b/src/include/gnunet_uri_lib.h index e5f144591..d428bdd9a 100644 --- a/src/include/gnunet_uri_lib.h +++ b/src/include/gnunet_uri_lib.h @@ -31,7 +31,7 @@ * Note: to make sure that no strings are copied, the first slash "/" in the * path will be used to null terminate the hostname if no port is supplied. */ -struct GNUNET_uri { +struct GNUNET_Uri { char *scheme; /* scheme, without ":" and "//" */ char *username; /* username, default: NULL */ char *password; /* password, default: NULL */ @@ -44,7 +44,7 @@ struct GNUNET_uri { /* A struct to hold the query string parameter values. */ -struct GNUNET_uri_param { +struct GNUNET_UriParam { char *key; char *val; }; @@ -70,7 +70,7 @@ struct GNUNET_uri_param { * Returns 0 on success, otherwise -1. */ int -GNUNET_uri_parse (struct GNUNET_uri *url, +GNUNET_uri_parse (struct GNUNET_Uri *url, char *url_str); @@ -115,7 +115,7 @@ GNUNET_uri_split_path (char *path, int GNUNET_uri_parse_query (char *query, char delimiter, - struct GNUNET_uri_param *params, + struct GNUNET_UriParam *params, int max_params); diff --git a/src/util/test_uri.c b/src/util/test_uri.c index f9e38383a..7c8156648 100644 --- a/src/util/test_uri.c +++ b/src/util/test_uri.c @@ -84,7 +84,7 @@ static char * test_parse_http_url_ok (void) { int rc; - struct GNUNET_uri url; + struct GNUNET_Uri url; char *url_string; /* Minimal URL */ @@ -397,7 +397,7 @@ static char * test_parse_http_rel_url_ok (void) { int rc; - struct GNUNET_uri url; + struct GNUNET_Uri url; char *url_string; /* Minimal relative URL */ @@ -492,7 +492,7 @@ static char * test_parse_url_fail (void) { int rc; - struct GNUNET_uri url; + struct GNUNET_Uri url; char *url_string; /* Empty */ @@ -627,7 +627,7 @@ test_parse_query_ok (void) { int rc; char *q; - struct GNUNET_uri_param params[10]; + struct GNUNET_UriParam params[10]; /* One param query */ q = strdup ("q=yes"); diff --git a/src/util/uri.c b/src/util/uri.c index ba5c0f716..87101d7e1 100644 --- a/src/util/uri.c +++ b/src/util/uri.c @@ -156,14 +156,14 @@ find_path (char *str) * Returns 0 on success, otherwise -1. */ int -GNUNET_uri_parse (struct GNUNET_uri *url, +GNUNET_uri_parse (struct GNUNET_Uri *url, char *u) { if (NULL == url || NULL == u) { return -1; } - memset(url, 0, sizeof (struct GNUNET_uri)); + memset(url, 0, sizeof (struct GNUNET_Uri)); /* (Fragment) */ url->fragment = find_fragment (u); @@ -311,7 +311,7 @@ GNUNET_uri_split_path (char *path, int GNUNET_uri_parse_query (char *query, char delimiter, - struct GNUNET_uri_param *params, + struct GNUNET_UriParam *params, int max_params) { int i = 0; -- cgit v1.2.3 From 3365ea7d4eb5d78a022b4b2d56813c0739e34cbb Mon Sep 17 00:00:00 2001 From: jospaeth Date: Fri, 28 Aug 2020 12:01:02 +0200 Subject: add gnsrecord-tvg and revocation-tvg to .gitignore --- src/gnsrecord/.gitignore | 1 + src/revocation/.gitignore | 1 + 2 files changed, 2 insertions(+) diff --git a/src/gnsrecord/.gitignore b/src/gnsrecord/.gitignore index 53d3bb22d..dca3bd309 100644 --- a/src/gnsrecord/.gitignore +++ b/src/gnsrecord/.gitignore @@ -3,3 +3,4 @@ test_gnsrecord_crypto test_gnsrecord_serialization zonefiles perf_gnsrecord_crypto +gnunet-gnsrecord-tvg diff --git a/src/revocation/.gitignore b/src/revocation/.gitignore index 9acd3ac33..1432f7922 100644 --- a/src/revocation/.gitignore +++ b/src/revocation/.gitignore @@ -2,3 +2,4 @@ gnunet-service-revocation gnunet-revocation test_revocation test_local_revocation.py +gnunet-revocation-tvg -- cgit v1.2.3 From 33d71863e30d8097c82b2450fd59b51744e3fe5f Mon Sep 17 00:00:00 2001 From: jospaeth Date: Fri, 28 Aug 2020 12:25:11 +0200 Subject: rename reclaim_attestation in POTFILES.in --- po/POTFILES.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/po/POTFILES.in b/po/POTFILES.in index 12e27fa81..2f37f5852 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -272,12 +272,12 @@ src/reclaim/gnunet-service-reclaim_tickets.c src/reclaim/json_reclaim.c src/reclaim/oidc_helper.c src/reclaim/plugin_gnsrecord_reclaim.c -src/reclaim/plugin_reclaim_attestation_jwt.c +src/reclaim/plugin_reclaim_credential_jwt.c src/reclaim/plugin_reclaim_attribute_basic.c src/reclaim/plugin_rest_openid_connect.c src/reclaim/plugin_rest_reclaim.c src/reclaim/reclaim_api.c -src/reclaim/reclaim_attestation.c +src/reclaim/reclaim_credential.c src/reclaim/reclaim_attribute.c src/regex/gnunet-daemon-regexprofiler.c src/regex/gnunet-regex-profiler.c -- cgit v1.2.3 From bbf9540c93da3c6b950920ee7eaae479c95403c5 Mon Sep 17 00:00:00 2001 From: jospaeth Date: Fri, 28 Aug 2020 12:26:52 +0200 Subject: fix order of namespaces in plugin_rest_identity GNUNET_REST_namespace_match() always matched to the first namespace for GET --- src/identity/plugin_rest_identity.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/identity/plugin_rest_identity.c b/src/identity/plugin_rest_identity.c index 4e32b73dd..d86d29e36 100644 --- a/src/identity/plugin_rest_identity.c +++ b/src/identity/plugin_rest_identity.c @@ -1296,13 +1296,13 @@ rest_process_request (struct GNUNET_REST_RequestHandle *rest_handle, struct RequestHandle *handle = GNUNET_new (struct RequestHandle); struct GNUNET_REST_RequestHandlerError err; static const struct GNUNET_REST_RequestHandler handlers[] = - { { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY, &ego_get_all }, - { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY_PUBKEY, + { { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY_PUBKEY, &ego_get_pubkey }, { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY_NAME, &ego_get_name }, { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY_SUBSYSTEM, &ego_get_subsystem }, + { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY, &ego_get_all }, { MHD_HTTP_METHOD_PUT, GNUNET_REST_API_NS_IDENTITY_PUBKEY, &ego_edit_pubkey }, -- cgit v1.2.3