2 This file is part of GNUnet.
3 (C) 2012 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
23 * @brief api for the set service
24 * @author Florian Dold
27 #include "gnunet_util_lib.h"
28 #include "gnunet_protocols.h"
29 #include "gnunet_client_lib.h"
30 #include "gnunet_set_service.h"
36 #define LOG(kind,...) GNUNET_log_from (kind, "set-api",__VA_ARGS__)
39 * Opaque handle to a set.
41 struct GNUNET_SET_Handle
43 struct GNUNET_CLIENT_Connection *client;
44 struct GNUNET_MQ_MessageQueue *mq;
45 unsigned int messages_since_ack;
49 * Opaque handle to a set operation request from another peer.
51 struct GNUNET_SET_Request
58 struct GNUNET_SET_OperationHandle
60 GNUNET_SET_ResultIterator result_cb;
62 struct GNUNET_SET_Handle *set;
64 GNUNET_SCHEDULER_TaskIdentifier timeout_task;
69 * Opaque handle to a listen operation.
71 struct GNUNET_SET_ListenHandle
73 struct GNUNET_CLIENT_Connection *client;
74 struct GNUNET_MQ_MessageQueue* mq;
75 GNUNET_SET_ListenCallback listen_cb;
81 * Handle result message for a set operation.
84 * @param mh the message
87 handle_result (void *cls, const struct GNUNET_MessageHeader *mh)
89 struct ResultMessage *msg = (struct ResultMessage *) mh;
90 struct GNUNET_SET_Handle *set = cls;
91 struct GNUNET_SET_OperationHandle *oh;
92 struct GNUNET_SET_Element e;
94 if (set->messages_since_ack >= GNUNET_SET_ACK_WINDOW/2)
96 struct GNUNET_MQ_Message *mqm;
97 mqm = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_ACK);
98 GNUNET_MQ_send (set->mq, mqm);
101 oh = GNUNET_MQ_assoc_get (set->mq, ntohl (msg->request_id));
102 GNUNET_assert (NULL != oh);
103 /* status is not STATUS_OK => there's no attached element,
104 * and this is the last result message we get */
105 if (htons (msg->result_status) != GNUNET_SET_STATUS_OK)
107 if (GNUNET_SCHEDULER_NO_TASK != oh->timeout_task)
109 GNUNET_SCHEDULER_cancel (oh->timeout_task);
110 oh->timeout_task = GNUNET_SCHEDULER_NO_TASK;
112 GNUNET_MQ_assoc_remove (set->mq, ntohl (msg->request_id));
113 if (NULL != oh->result_cb)
114 oh->result_cb (oh->result_cls, NULL, htons (msg->result_status));
120 e.size = ntohs (mh->size) - sizeof (struct ResultMessage);
121 e.type = msg->element_type;
122 if (NULL != oh->result_cb)
123 oh->result_cb (oh->result_cls, &e, htons (msg->result_status));
127 * Handle request message for a listen operation
129 * @param cls the listen handle
130 * @param mh the message
133 handle_request (void *cls, const struct GNUNET_MessageHeader *mh)
135 struct RequestMessage *msg = (struct RequestMessage *) mh;
136 struct GNUNET_SET_ListenHandle *lh = cls;
137 struct GNUNET_SET_Request *req;
139 req = GNUNET_new (struct GNUNET_SET_Request);
140 req->accept_id = ntohl (msg->accept_id);
141 /* calling GNUNET_SET_accept in the listen cb will set req->accepted */
142 lh->listen_cb (lh->listen_cls, &msg->peer_id, &mh[1], req);
144 if (GNUNET_NO == req->accepted)
146 struct GNUNET_MQ_Message *mqm;
147 struct AcceptMessage *amsg;
149 mqm = GNUNET_MQ_msg (amsg, GNUNET_MESSAGE_TYPE_SET_ACCEPT);
150 /* no request id, as we refused */
151 amsg->request_id = htonl (0);
152 amsg->accept_id = msg->accept_id;
153 GNUNET_MQ_send (lh->mq, mqm);
157 /* the accept-case is handled in GNUNET_SET_accept,
158 * as we have the accept message available there */
163 * Create an empty set, supporting the specified operation.
165 * @param cfg configuration to use for connecting to the
167 * @param op operation supported by the set
168 * Note that the operation has to be specified
169 * beforehand, as certain set operations need to maintain
170 * data structures spefific to the operation
171 * @return a handle to the set
173 struct GNUNET_SET_Handle *
174 GNUNET_SET_create (const struct GNUNET_CONFIGURATION_Handle *cfg,
175 enum GNUNET_SET_OperationType op)
177 struct GNUNET_SET_Handle *set;
178 struct GNUNET_MQ_Message *mqm;
179 struct SetCreateMessage *msg;
180 static const struct GNUNET_MQ_Handler mq_handlers[] = {
181 {handle_result, GNUNET_MESSAGE_TYPE_SET_RESULT},
182 GNUNET_MQ_HANDLERS_END
185 set = GNUNET_new (struct GNUNET_SET_Handle);
186 set->client = GNUNET_CLIENT_connect ("set", cfg);
187 LOG (GNUNET_ERROR_TYPE_INFO, "set client created\n");
188 GNUNET_assert (NULL != set->client);
189 set->mq = GNUNET_MQ_queue_for_connection_client (set->client, mq_handlers, set);
190 mqm = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SET_CREATE);
191 msg->operation = htons (op);
192 GNUNET_MQ_send (set->mq, mqm);
198 * Add an element to the given set.
199 * After the element has been added (in the sense of being
200 * transmitted to the set service), cont will be called.
201 * Calls to add_element can be queued
203 * @param set set to add element to
204 * @param element element to add to the set
205 * @param cont continuation called after the element has been added
206 * @param cont_cls closure for cont
209 GNUNET_SET_add_element (struct GNUNET_SET_Handle *set,
210 const struct GNUNET_SET_Element *element,
211 GNUNET_SET_Continuation cont,
214 struct GNUNET_MQ_Message *mqm;
215 struct ElementMessage *msg;
217 mqm = GNUNET_MQ_msg_extra (msg, element->size, GNUNET_MESSAGE_TYPE_SET_ADD);
218 msg->element_type = element->type;
219 memcpy (&msg[1], element->data, element->size);
220 GNUNET_MQ_notify_sent (mqm, cont, cont_cls);
221 GNUNET_MQ_send (set->mq, mqm);
226 * Remove an element to the given set.
227 * After the element has been removed (in the sense of the
228 * request being transmitted to the set service), cont will be called.
229 * Calls to remove_element can be queued
231 * @param set set to remove element from
232 * @param element element to remove from the set
233 * @param cont continuation called after the element has been removed
234 * @param cont_cls closure for cont
237 GNUNET_SET_remove_element (struct GNUNET_SET_Handle *set,
238 const struct GNUNET_SET_Element *element,
239 GNUNET_SET_Continuation cont,
242 struct GNUNET_MQ_Message *mqm;
243 struct ElementMessage *msg;
245 mqm = GNUNET_MQ_msg_extra (msg, element->size, GNUNET_MESSAGE_TYPE_SET_REMOVE);
246 msg->element_type = element->type;
247 memcpy (&msg[1], element->data, element->size);
248 GNUNET_MQ_notify_sent (mqm, cont, cont_cls);
249 GNUNET_MQ_send (set->mq, mqm);
254 * Destroy the set handle, and free all associated resources.
257 GNUNET_SET_destroy (struct GNUNET_SET_Handle *set)
259 GNUNET_CLIENT_disconnect (set->client);
261 GNUNET_MQ_destroy (set->mq);
267 * Signature of the main function of a task.
270 * @param tc context information (why was this task triggered now)
273 operation_timeout_task (void *cls,
274 const struct GNUNET_SCHEDULER_TaskContext * tc)
276 struct GNUNET_SET_OperationHandle *oh = cls;
277 oh->timeout_task = GNUNET_SCHEDULER_NO_TASK;
278 if (NULL != oh->result_cb)
279 oh->result_cb (oh->result_cls, NULL, GNUNET_SET_STATUS_TIMEOUT);
280 oh->result_cb = NULL;
281 oh->result_cls = NULL;
282 GNUNET_SET_operation_cancel (oh);
287 * Evaluate a set operation with our set and the set of another peer.
289 * @param set set to use
290 * @param salt salt for HKDF (explain more here)
291 * @param other_peer peer with the other set
292 * @param app_id hash for the application using the set
293 * @param context_msg additional information for the request
294 * @param salt salt used for the set operation; sometimes set operations
295 * fail due to hash collisions, using a different salt for each operation
296 * makes it harder for an attacker to exploit this
297 * @param timeout result_cb will be called with GNUNET_SET_STATUS_TIMEOUT
298 * if the operation is not done after the specified time
299 * @param result_mode specified how results will be returned,
300 * see 'GNUNET_SET_ResultMode'.
301 * @param result_cb called on error or success
302 * @param result_cls closure for result_cb
303 * @return a handle to cancel the operation
305 struct GNUNET_SET_OperationHandle *
306 GNUNET_SET_evaluate (struct GNUNET_SET_Handle *set,
307 const struct GNUNET_PeerIdentity *other_peer,
308 const struct GNUNET_HashCode *app_id,
309 const struct GNUNET_MessageHeader *context_msg,
311 struct GNUNET_TIME_Relative timeout,
312 enum GNUNET_SET_ResultMode result_mode,
313 GNUNET_SET_ResultIterator result_cb,
316 struct GNUNET_MQ_Message *mqm;
317 struct EvaluateMessage *msg;
318 struct GNUNET_SET_OperationHandle *oh;
320 oh = GNUNET_new (struct GNUNET_SET_OperationHandle);
321 oh->result_cb = result_cb;
322 oh->result_cls = result_cls;
325 mqm = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SET_EVALUATE);
326 msg->request_id = htonl (GNUNET_MQ_assoc_add (set->mq, mqm, oh));
327 msg->peer = *other_peer;
328 msg->app_id = *app_id;
330 if (NULL != context_msg)
331 if (GNUNET_OK != GNUNET_MQ_nest (mqm, context_msg, ntohs (context_msg->size)))
334 oh->timeout_task = GNUNET_SCHEDULER_add_delayed (timeout, operation_timeout_task, oh);
335 GNUNET_MQ_send (set->mq, mqm);
342 * Wait for set operation requests for the given application id
344 * @param cfg configuration to use for connecting to
346 * @param operation operation we want to listen for
347 * @param app_id id of the application that handles set operation requests
348 * @param listen_cb called for each incoming request matching the operation
350 * @param listen_cls handle for listen_cb
351 * @return a handle that can be used to cancel the listen operation
353 struct GNUNET_SET_ListenHandle *
354 GNUNET_SET_listen (const struct GNUNET_CONFIGURATION_Handle *cfg,
355 enum GNUNET_SET_OperationType operation,
356 const struct GNUNET_HashCode *app_id,
357 GNUNET_SET_ListenCallback listen_cb,
360 struct GNUNET_SET_ListenHandle *lh;
361 struct GNUNET_MQ_Message *mqm;
362 struct ListenMessage *msg;
363 static const struct GNUNET_MQ_Handler mq_handlers[] = {
364 {handle_request, GNUNET_MESSAGE_TYPE_SET_REQUEST},
365 GNUNET_MQ_HANDLERS_END
368 lh = GNUNET_new (struct GNUNET_SET_ListenHandle);
369 lh->client = GNUNET_CLIENT_connect ("set", cfg);
370 lh->listen_cb = listen_cb;
371 lh->listen_cls = listen_cls;
372 GNUNET_assert (NULL != lh->client);
373 lh->mq = GNUNET_MQ_queue_for_connection_client (lh->client, mq_handlers, lh);
374 mqm = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SET_LISTEN);
375 msg->operation = htons (operation);
376 msg->app_id = *app_id;
377 GNUNET_MQ_send (lh->mq, mqm);
384 * Cancel the given listen operation.
386 * @param lh handle for the listen operation
389 GNUNET_SET_listen_cancel (struct GNUNET_SET_ListenHandle *lh)
391 GNUNET_CLIENT_disconnect (lh->client);
392 GNUNET_MQ_destroy (lh->mq);
398 * Accept a request we got via GNUNET_SET_listen.
400 * @param request request to accept
401 * @param set set used for the requested operation
402 * @param timeout timeout for the set operation
403 * @param result_mode specified how results will be returned,
404 * see 'GNUNET_SET_ResultMode'.
405 * @param result_cb callback for the results
406 * @param result_cls closure for result_cb
407 * @return a handle to cancel the operation
409 struct GNUNET_SET_OperationHandle *
410 GNUNET_SET_accept (struct GNUNET_SET_Request *request,
411 struct GNUNET_SET_Handle *set,
412 struct GNUNET_TIME_Relative timeout,
413 enum GNUNET_SET_ResultMode result_mode,
414 GNUNET_SET_ResultIterator result_cb,
417 struct GNUNET_MQ_Message *mqm;
418 struct AcceptMessage *msg;
419 struct GNUNET_SET_OperationHandle *oh;
421 /* don't accept a request twice! */
422 GNUNET_assert (GNUNET_NO == request->accepted);
423 request->accepted = GNUNET_YES;
425 oh = GNUNET_new (struct GNUNET_SET_OperationHandle);
426 oh->result_cb = result_cb;
427 oh->result_cls = result_cls;
430 mqm = GNUNET_MQ_msg (msg , GNUNET_MESSAGE_TYPE_SET_ACCEPT);
431 msg->request_id = htonl (GNUNET_MQ_assoc_add (set->mq, NULL, oh));
432 msg->accept_id = htonl (request->accept_id);
433 GNUNET_MQ_send (set->mq, mqm);
435 oh->timeout_task = GNUNET_SCHEDULER_add_delayed (timeout, operation_timeout_task, oh);
442 * Cancel the given set operation.
444 * @param oh set operation to cancel
447 GNUNET_SET_operation_cancel (struct GNUNET_SET_OperationHandle *oh)
449 struct GNUNET_MQ_Message *mqm;
450 struct GNUNET_SET_OperationHandle *h_assoc;
452 h_assoc = GNUNET_MQ_assoc_remove (oh->set->mq, oh->request_id);
453 GNUNET_assert (h_assoc == oh);
454 mqm = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_CANCEL);
455 GNUNET_MQ_send (oh->set->mq, mqm);