X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fset%2Fset_api.c;h=04a4e49108f6a7d2661f369d989fb78314f5839c;hb=9528dcc4b739041f51ed0c8791fe34902525fac2;hp=6cbee9a5722f53c56880e254ef88d9585adfe43a;hpb=a11e17158609766f29c40de6daecf5ce02b38e6c;p=oweals%2Fgnunet.git diff --git a/src/set/set_api.c b/src/set/set_api.c index 6cbee9a57..04a4e4910 100644 --- a/src/set/set_api.c +++ b/src/set/set_api.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2012-2014 Christian Grothoff (and other contributing authors) + Copyright (C) 2012-2016 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -26,7 +26,6 @@ #include "platform.h" #include "gnunet_util_lib.h" #include "gnunet_protocols.h" -#include "gnunet_client_lib.h" #include "gnunet_set_service.h" #include "set.h" @@ -49,11 +48,6 @@ struct SetCopyRequest */ struct GNUNET_SET_Handle { - /** - * Client connected to the set service. - */ - struct GNUNET_CLIENT_Connection *client; - /** * Message queue for @e client. */ @@ -189,10 +183,6 @@ struct GNUNET_SET_OperationHandle */ struct GNUNET_SET_ListenHandle { - /** - * Connection to the service. - */ - struct GNUNET_CLIENT_Connection *client; /** * Message queue for the client. @@ -230,7 +220,7 @@ struct GNUNET_SET_ListenHandle /** * Task for reconnecting when the listener fails. */ - struct GNUNET_SCHEDULER_Task * reconnect_task; + struct GNUNET_SCHEDULER_Task *reconnect_task; /** * Operation we listen for. @@ -243,7 +233,7 @@ struct GNUNET_SET_ListenHandle static struct GNUNET_SET_Handle * create_internal (const struct GNUNET_CONFIGURATION_Handle *cfg, enum GNUNET_SET_OperationType op, - uint32_t *cookie); + const uint32_t *cookie); /** @@ -251,21 +241,17 @@ create_internal (const struct GNUNET_CONFIGURATION_Handle *cfg, * iterator and sends an acknowledgement to the service. * * @param cls the `struct GNUNET_SET_Handle *` - * @param mh the message + * @param msg the message */ static void handle_copy_lazy (void *cls, - const struct GNUNET_MessageHeader *mh) + const struct GNUNET_SET_CopyLazyResponseMessage *msg) { - struct GNUNET_SET_CopyLazyResponseMessage *msg; struct GNUNET_SET_Handle *set = cls; struct SetCopyRequest *req; struct GNUNET_SET_Handle *new_set; - msg = (struct GNUNET_SET_CopyLazyResponseMessage *) mh; - req = set->copy_req_head; - if (NULL == req) { /* Service sent us unsolicited lazy copy response */ @@ -275,22 +261,35 @@ handle_copy_lazy (void *cls, LOG (GNUNET_ERROR_TYPE_DEBUG, "Handling response to lazy copy\n"); - GNUNET_CONTAINER_DLL_remove (set->copy_req_head, set->copy_req_tail, req); - - // We pass none as operation here, since it doesn't matter when // cloning. - new_set = create_internal (set->cfg, GNUNET_SET_OPERATION_NONE, &msg->cookie); - + new_set = create_internal (set->cfg, + GNUNET_SET_OPERATION_NONE, + &msg->cookie); req->cb (req->cls, new_set); - GNUNET_free (req); } +/** + * 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_iter_element (void *cls, + const struct GNUNET_SET_IterResponseMessage *msg) +{ + /* minimum size was already checked, everything else is OK! */ + return GNUNET_OK; +} + + /** * Handle element for iteration over the set. Notifies the * iterator and sends an acknowledgement to the service. @@ -300,28 +299,16 @@ handle_copy_lazy (void *cls, */ static void handle_iter_element (void *cls, - const struct GNUNET_MessageHeader *mh) + const struct GNUNET_SET_IterResponseMessage *msg) { struct GNUNET_SET_Handle *set = cls; GNUNET_SET_ElementIterator iter = set->iterator; struct GNUNET_SET_Element element; - const struct GNUNET_SET_IterResponseMessage *msg; struct GNUNET_SET_IterAckMessage *ack_msg; struct GNUNET_MQ_Envelope *ev; uint16_t msize; - msize = ntohs (mh->size); - if (msize < sizeof (sizeof (struct GNUNET_SET_IterResponseMessage))) - { - /* message malformed */ - GNUNET_break (0); - set->iterator = NULL; - set->iteration_id++; - iter (set->iterator_cls, - NULL); - iter = NULL; - } - msg = (const struct GNUNET_SET_IterResponseMessage *) mh; + msize = ntohs (msg->header.size); if (set->iteration_id != ntohs (msg->iteration_id)) { /* element from a previous iteration, skip! */ @@ -330,7 +317,7 @@ handle_iter_element (void *cls, if (NULL != iter) { element.size = msize - sizeof (struct GNUNET_SET_IterResponseMessage); - element.element_type = htons (msg->element_type); + element.element_type = ntohs (msg->element_type); element.data = &msg[1]; iter (set->iterator_cls, &element); @@ -362,6 +349,25 @@ handle_iter_done (void *cls, set->iteration_id++; iter (set->iterator_cls, NULL); + + if (GNUNET_YES == set->destroy_requested) + GNUNET_SET_destroy (set); +} + + +/** + * 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_SET_ResultMessage *msg) +{ + /* minimum size was already checked, everything else is OK! */ + return GNUNET_OK; } @@ -373,15 +379,14 @@ handle_iter_done (void *cls, */ static void handle_result (void *cls, - const struct GNUNET_MessageHeader *mh) + const struct GNUNET_SET_ResultMessage *msg) { struct GNUNET_SET_Handle *set = cls; - const struct GNUNET_SET_ResultMessage *msg; struct GNUNET_SET_OperationHandle *oh; struct GNUNET_SET_Element e; enum GNUNET_SET_Status result_status; + int destroy_set; - msg = (const struct GNUNET_SET_ResultMessage *) mh; GNUNET_assert (NULL != set->mq); result_status = ntohs (msg->result_status); LOG (GNUNET_ERROR_TYPE_DEBUG, @@ -421,10 +426,16 @@ do_final: 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 @@ -432,8 +443,7 @@ do_final: LOG (GNUNET_ERROR_TYPE_DEBUG, "No callback for final status\n"); } - if ( (GNUNET_YES == set->destroy_requested) && - (NULL == set->ops_head) ) + if (destroy_set) GNUNET_SET_destroy (set); GNUNET_free (oh); return; @@ -442,11 +452,12 @@ do_element: LOG (GNUNET_ERROR_TYPE_DEBUG, "Treating result as element\n"); e.data = &msg[1]; - e.size = ntohs (mh->size) - sizeof (struct GNUNET_SET_ResultMessage); - e.element_type = msg->element_type; + e.size = ntohs (msg->header.size) - sizeof (struct GNUNET_SET_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); } @@ -522,8 +533,9 @@ handle_client_set_error (void *cls, enum GNUNET_MQ_Error error) { struct GNUNET_SET_Handle *set = cls; + GNUNET_SET_ElementIterator iter = set->iterator; - LOG (GNUNET_ERROR_TYPE_DEBUG, + LOG (GNUNET_ERROR_TYPE_ERROR, "Handling client set error %d\n", error); while (NULL != set->ops_head) @@ -531,58 +543,59 @@ handle_client_set_error (void *cls, if (NULL != set->ops_head->result_cb) set->ops_head->result_cb (set->ops_head->result_cls, NULL, + 0, GNUNET_SET_STATUS_FAILURE); set_operation_destroy (set->ops_head); } + set->iterator = NULL; + set->iteration_id++; set->invalid = GNUNET_YES; - if (GNUNET_YES == set->destroy_requested) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Destroying set after operation failure\n"); - GNUNET_SET_destroy (set); - } + if (NULL != iter) + iter (set->iterator_cls, + NULL); } static struct GNUNET_SET_Handle * create_internal (const struct GNUNET_CONFIGURATION_Handle *cfg, enum GNUNET_SET_OperationType op, - uint32_t *cookie) + const uint32_t *cookie) { - static const struct GNUNET_MQ_MessageHandler mq_handlers[] = { - { &handle_result, - GNUNET_MESSAGE_TYPE_SET_RESULT, - 0 }, - { &handle_iter_element, - GNUNET_MESSAGE_TYPE_SET_ITER_ELEMENT, - 0 }, - { &handle_iter_done, - GNUNET_MESSAGE_TYPE_SET_ITER_DONE, - sizeof (struct GNUNET_MessageHeader) }, - { &handle_copy_lazy, - GNUNET_MESSAGE_TYPE_SET_COPY_LAZY_RESPONSE, - sizeof (struct GNUNET_SET_CopyLazyResponseMessage) }, - GNUNET_MQ_HANDLERS_END + struct GNUNET_SET_Handle *set = GNUNET_new (struct GNUNET_SET_Handle); + struct GNUNET_MQ_MessageHandler mq_handlers[] = { + GNUNET_MQ_hd_var_size (result, + GNUNET_MESSAGE_TYPE_SET_RESULT, + struct GNUNET_SET_ResultMessage, + set), + GNUNET_MQ_hd_var_size (iter_element, + GNUNET_MESSAGE_TYPE_SET_ITER_ELEMENT, + struct GNUNET_SET_IterResponseMessage, + set), + GNUNET_MQ_hd_fixed_size (iter_done, + GNUNET_MESSAGE_TYPE_SET_ITER_DONE, + struct GNUNET_MessageHeader, + set), + GNUNET_MQ_hd_fixed_size (copy_lazy, + GNUNET_MESSAGE_TYPE_SET_COPY_LAZY_RESPONSE, + struct GNUNET_SET_CopyLazyResponseMessage, + set), + GNUNET_MQ_handler_end () }; - struct GNUNET_SET_Handle *set; struct GNUNET_MQ_Envelope *mqm; struct GNUNET_SET_CreateMessage *create_msg; struct GNUNET_SET_CopyLazyConnectMessage *copy_msg; - set = GNUNET_new (struct GNUNET_SET_Handle); - set->client = GNUNET_CLIENT_connect ("set", cfg); set->cfg = cfg; - if (NULL == set->client) + set->mq = GNUNET_CLIENT_connect (cfg, + "set", + mq_handlers, + &handle_client_set_error, + set); + if (NULL == set->mq) { GNUNET_free (set); return NULL; } - set->mq = GNUNET_MQ_queue_for_connection_client (set->client, - mq_handlers, - &handle_client_set_error, - set); - GNUNET_assert (NULL != set->mq); - if (NULL == cookie) { LOG (GNUNET_ERROR_TYPE_DEBUG, @@ -647,18 +660,21 @@ GNUNET_SET_add_element (struct GNUNET_SET_Handle *set, struct GNUNET_MQ_Envelope *mqm; struct GNUNET_SET_ElementMessage *msg; + LOG (GNUNET_ERROR_TYPE_INFO, "adding element of type %u\n", (unsigned) element->element_type); + if (GNUNET_YES == set->invalid) { if (NULL != cont) cont (cont_cls); return GNUNET_SYSERR; } - mqm = GNUNET_MQ_msg_extra (msg, element->size, + mqm = GNUNET_MQ_msg_extra (msg, + element->size, GNUNET_MESSAGE_TYPE_SET_ADD); - msg->element_type = element->element_type; - memcpy (&msg[1], - element->data, - element->size); + msg->element_type = htons (element->element_type); + GNUNET_memcpy (&msg[1], + element->data, + element->size); GNUNET_MQ_notify_sent (mqm, cont, cont_cls); GNUNET_MQ_send (set->mq, mqm); @@ -697,10 +713,10 @@ GNUNET_SET_remove_element (struct GNUNET_SET_Handle *set, mqm = GNUNET_MQ_msg_extra (msg, element->size, GNUNET_MESSAGE_TYPE_SET_REMOVE); - msg->element_type = element->element_type; - memcpy (&msg[1], - element->data, - element->size); + msg->element_type = htons (element->element_type); + GNUNET_memcpy (&msg[1], + element->data, + element->size); GNUNET_MQ_notify_sent (mqm, cont, cont_cls); GNUNET_MQ_send (set->mq, mqm); @@ -720,8 +736,7 @@ GNUNET_SET_destroy (struct GNUNET_SET_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->iterator); - if (NULL != set->ops_head) + if ( (NULL != set->ops_head) || (NULL != set->iterator) ) { LOG (GNUNET_ERROR_TYPE_DEBUG, "Set operations are pending, delaying set destruction\n"); @@ -730,11 +745,6 @@ GNUNET_SET_destroy (struct GNUNET_SET_Handle *set) } LOG (GNUNET_ERROR_TYPE_DEBUG, "Really destroying set\n"); - if (NULL != set->client) - { - GNUNET_CLIENT_disconnect (set->client); - set->client = NULL; - } if (NULL != set->mq) { GNUNET_MQ_destroy (set->mq); @@ -763,13 +773,18 @@ GNUNET_SET_prepare (const struct GNUNET_PeerIdentity *other_peer, const struct GNUNET_HashCode *app_id, const struct GNUNET_MessageHeader *context_msg, enum GNUNET_SET_ResultMode result_mode, + struct GNUNET_SET_Option options[], GNUNET_SET_ResultIterator result_cb, void *result_cls) { struct GNUNET_MQ_Envelope *mqm; struct GNUNET_SET_OperationHandle *oh; struct GNUNET_SET_EvaluateMessage *msg; + struct GNUNET_SET_Option *opt; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Client prepares set operation (%d)\n", + result_mode); oh = GNUNET_new (struct GNUNET_SET_OperationHandle); oh->result_cb = result_cb; oh->result_cls = result_cls; @@ -779,6 +794,25 @@ GNUNET_SET_prepare (const struct GNUNET_PeerIdentity *other_peer, msg->app_id = *app_id; msg->result_mode = htonl (result_mode); msg->target_peer = *other_peer; + for (opt = options; opt->type != 0; opt++) + { + switch (opt->type) + { + case GNUNET_SET_OPTION_BYZANTINE: + msg->byzantine = GNUNET_YES; + msg->byzantine_lower_bound = opt->v.num; + break; + case GNUNET_SET_OPTION_FORCE_FULL: + msg->force_full = GNUNET_YES; + break; + case GNUNET_SET_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; @@ -790,49 +824,57 @@ GNUNET_SET_prepare (const struct GNUNET_PeerIdentity *other_peer, * Connect to the set service in order to listen for requests. * * @param cls the `struct GNUNET_SET_ListenHandle *` to connect - * @param tc task context if invoked as a task, NULL otherwise */ static void -listen_connect (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc); +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_SET_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 mh the message + * @param msg the message */ static void handle_request (void *cls, - const struct GNUNET_MessageHeader *mh) + const struct GNUNET_SET_RequestMessage *msg) { struct GNUNET_SET_ListenHandle *lh = cls; - const struct GNUNET_SET_RequestMessage *msg; struct GNUNET_SET_Request req; const struct GNUNET_MessageHeader *context_msg; - uint16_t msize; struct GNUNET_MQ_Envelope *mqm; struct GNUNET_SET_RejectMessage *rmsg; LOG (GNUNET_ERROR_TYPE_DEBUG, "Processing incoming operation request\n"); - msize = ntohs (mh->size); - if (msize < sizeof (struct GNUNET_SET_RequestMessage)) - { - GNUNET_break (0); - GNUNET_CLIENT_disconnect (lh->client); - lh->client = NULL; - 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); - return; - } /* we got another valid request => reset the backoff */ lh->reconnect_backoff = GNUNET_TIME_UNIT_MILLISECONDS; - msg = (const struct GNUNET_SET_RequestMessage *) mh; req.accept_id = ntohl (msg->accept_id); req.accepted = GNUNET_NO; context_msg = GNUNET_MQ_extract_nested_mh (msg); @@ -868,12 +910,11 @@ handle_client_listener_error (void *cls, LOG (GNUNET_ERROR_TYPE_DEBUG, "Listener broke down (%d), re-connecting\n", (int) error); - GNUNET_CLIENT_disconnect (lh->client); - lh->client = NULL; GNUNET_MQ_destroy (lh->mq); lh->mq = NULL; lh->reconnect_task = GNUNET_SCHEDULER_add_delayed (lh->reconnect_backoff, - &listen_connect, lh); + &listen_connect, + lh); lh->reconnect_backoff = GNUNET_TIME_STD_BACKOFF (lh->reconnect_backoff); } @@ -882,40 +923,35 @@ handle_client_listener_error (void *cls, * Connect to the set service in order to listen for requests. * * @param cls the `struct GNUNET_SET_ListenHandle *` to connect - * @param tc task context if invoked as a task, NULL otherwise */ static void -listen_connect (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) +listen_connect (void *cls) { - static const struct GNUNET_MQ_MessageHandler mq_handlers[] = { - { &handle_request, GNUNET_MESSAGE_TYPE_SET_REQUEST }, - GNUNET_MQ_HANDLERS_END - }; struct GNUNET_SET_ListenHandle *lh = cls; + struct GNUNET_MQ_MessageHandler mq_handlers[] = { + GNUNET_MQ_hd_var_size (request, + GNUNET_MESSAGE_TYPE_SET_REQUEST, + struct GNUNET_SET_RequestMessage, + lh), + GNUNET_MQ_handler_end () + }; struct GNUNET_MQ_Envelope *mqm; struct GNUNET_SET_ListenMessage *msg; - if ( (NULL != tc) && - (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) ) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Listener not reconnecting due to shutdown\n"); - return; - } lh->reconnect_task = NULL; - GNUNET_assert (NULL == lh->client); - lh->client = GNUNET_CLIENT_connect ("set", lh->cfg); - if (NULL == lh->client) - return; GNUNET_assert (NULL == lh->mq); - lh->mq = GNUNET_MQ_queue_for_connection_client (lh->client, - mq_handlers, - &handle_client_listener_error, lh); + 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_SET_LISTEN); msg->operation = htonl (lh->operation); msg->app_id = lh->app_id; - GNUNET_MQ_send (lh->mq, mqm); + GNUNET_MQ_send (lh->mq, + mqm); } @@ -947,8 +983,8 @@ GNUNET_SET_listen (const struct GNUNET_CONFIGURATION_Handle *cfg, lh->operation = operation; lh->app_id = *app_id; lh->reconnect_backoff = GNUNET_TIME_UNIT_MILLISECONDS; - listen_connect (lh, NULL); - if (NULL == lh->client) + listen_connect (lh); + if (NULL == lh->mq) { GNUNET_free (lh); return NULL; @@ -972,11 +1008,6 @@ GNUNET_SET_listen_cancel (struct GNUNET_SET_ListenHandle *lh) GNUNET_MQ_destroy (lh->mq); lh->mq = NULL; } - if (NULL != lh->client) - { - GNUNET_CLIENT_disconnect (lh->client); - lh->client = NULL; - } if (NULL != lh->reconnect_task) { GNUNET_SCHEDULER_cancel (lh->reconnect_task); @@ -1003,6 +1034,7 @@ GNUNET_SET_listen_cancel (struct GNUNET_SET_ListenHandle *lh) struct GNUNET_SET_OperationHandle * GNUNET_SET_accept (struct GNUNET_SET_Request *request, enum GNUNET_SET_ResultMode result_mode, + struct GNUNET_SET_Option options[], GNUNET_SET_ResultIterator result_cb, void *result_cls) { @@ -1011,6 +1043,9 @@ GNUNET_SET_accept (struct GNUNET_SET_Request *request, struct GNUNET_SET_AcceptMessage *msg; GNUNET_assert (GNUNET_NO == request->accepted); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Client accepts set operation (%d)\n", + result_mode); request->accepted = GNUNET_YES; mqm = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SET_ACCEPT); msg->accept_reject_id = htonl (request->accept_id); @@ -1041,17 +1076,27 @@ int GNUNET_SET_commit (struct GNUNET_SET_OperationHandle *oh, struct GNUNET_SET_Handle *set) { - GNUNET_assert (NULL == oh->set); + if (NULL != oh->set) + { + /* Some other set was already commited for this + * operation, there is a logic bug in the client of this API */ + GNUNET_break (0); + return GNUNET_OK; + } 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 = GNUNET_MQ_assoc_add (set->mq, + oh); *oh->request_id_addr = htonl (oh->request_id); - GNUNET_MQ_send (set->mq, oh->conclude_mqm); + GNUNET_MQ_send (set->mq, + oh->conclude_mqm); oh->conclude_mqm = NULL; oh->request_id_addr = NULL; return GNUNET_OK; @@ -1128,8 +1173,9 @@ GNUNET_SET_element_dup (const struct GNUNET_SET_Element *element) copy->size = element->size; copy->element_type = element->element_type; copy->data = ©[1]; - memcpy ((void *) copy->data, element->data, copy->size); - + GNUNET_memcpy (©[1], + element->data, + copy->size); return copy; } @@ -1138,14 +1184,21 @@ GNUNET_SET_element_dup (const struct GNUNET_SET_Element *element) * Hash a set element. * * @param element the element that should be hashed - * @param ret_hash a pointer to where the hash of @a element + * @param[out] ret_hash a pointer to where the hash of @a element * should be stored */ void -GNUNET_SET_element_hash (const struct GNUNET_SET_Element *element, struct GNUNET_HashCode *ret_hash) +GNUNET_SET_element_hash (const struct GNUNET_SET_Element *element, + struct GNUNET_HashCode *ret_hash) { - /* FIXME: The element type should also be hashed. */ - GNUNET_CRYPTO_hash (element->data, element->size, 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 set_api.c */