- fixed malloc size
[oweals/gnunet.git] / src / set / set_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012, 2013 Christian Grothoff (and other contributing authors)
4
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.
9
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.
14
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.
19 */
20
21 /**
22  * @file set/set_api.c
23  * @brief api for the set service
24  * @author Florian Dold
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_protocols.h"
29 #include "gnunet_client_lib.h"
30 #include "gnunet_set_service.h"
31 #include "set.h"
32
33
34 #define LOG(kind,...) GNUNET_log_from (kind, "set-api",__VA_ARGS__)
35
36 /**
37  * Opaque handle to a set.
38  */
39 struct GNUNET_SET_Handle
40 {
41   struct GNUNET_CLIENT_Connection *client;
42   struct GNUNET_MQ_Handle *mq;
43   unsigned int messages_since_ack;
44   struct GNUNET_SET_OperationHandle *ops_head;
45   struct GNUNET_SET_OperationHandle *ops_tail;
46   int destroy_requested;
47 };
48
49
50 /**
51  * Opaque handle to a set operation request from another peer.
52  */
53 struct GNUNET_SET_Request
54 {
55   /**
56    * Id of the request, used to identify the request when
57    * accepting/rejecting it.
58    */
59   uint32_t accept_id;
60
61   /**
62    * Has the request been accepted already?
63    * GNUNET_YES/GNUNET_NO
64    */
65   int accepted;
66 };
67
68
69 /**
70  * Handle to an operation.
71  * Only known to the service after commiting
72  * the handle with a set.
73  */
74 struct GNUNET_SET_OperationHandle
75 {
76   /**
77    * Function to be called when we have a result,
78    * or an error.
79    */
80   GNUNET_SET_ResultIterator result_cb;
81
82   /**
83    * Closure for result_cb.
84    */
85   void *result_cls;
86
87   /**
88    * Local set used for the operation,
89    * NULL if no set has been provided by conclude yet.
90    */
91   struct GNUNET_SET_Handle *set;
92
93   /**
94    * Request ID to identify the operation within the set.
95    */
96   uint32_t request_id;
97
98   /**
99    * Message sent to the server on calling conclude,
100    * NULL if conclude has been called.
101    */
102   struct GNUNET_MQ_Envelope *conclude_mqm;
103
104   /**
105    * Address of the request if in the conclude message,
106    * used to patch the request id into the message when the set is known.
107    */
108   uint32_t *request_id_addr;
109
110   /**
111    * Handles are kept in a linked list.
112    */
113   struct GNUNET_SET_OperationHandle *prev;
114
115   /**
116    * Handles are kept in a linked list.
117    */
118   struct GNUNET_SET_OperationHandle *next;
119
120 };
121
122
123 /**
124  * Opaque handle to a listen operation.
125  */
126 struct GNUNET_SET_ListenHandle
127 {
128   /**
129    * Connection to the service.
130    */
131   struct GNUNET_CLIENT_Connection *client;
132
133   /**
134    * Message queue for the client.
135    */
136   struct GNUNET_MQ_Handle* mq;
137
138   /**
139    * Function to call on a new incoming request,
140    * or on error.
141    */
142   GNUNET_SET_ListenCallback listen_cb;
143
144   /**
145    * Closure for listen_cb.
146    */
147   void *listen_cls;
148 };
149
150
151 /**
152  * Handle result message for a set operation.
153  *
154  * @param cls the set
155  * @param mh the message
156  */
157 static void
158 handle_result (void *cls, const struct GNUNET_MessageHeader *mh)
159 {
160   const struct GNUNET_SET_ResultMessage *msg;
161   struct GNUNET_SET_Handle *set = cls;
162   struct GNUNET_SET_OperationHandle *oh;
163   struct GNUNET_SET_Element e;
164   enum GNUNET_SET_Status result_status;
165
166   msg = (const struct GNUNET_SET_ResultMessage *) mh;
167   GNUNET_assert (NULL != set);
168   GNUNET_assert (NULL != set->mq);
169
170   result_status = ntohs (msg->result_status);
171
172   if (set->messages_since_ack >= GNUNET_SET_ACK_WINDOW/2)
173   {
174     struct GNUNET_MQ_Envelope *mqm;
175     mqm = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_ACK);
176     GNUNET_MQ_send (set->mq, mqm);
177   }
178   oh = GNUNET_MQ_assoc_get (set->mq, ntohl (msg->request_id));
179   GNUNET_assert (NULL != oh);
180   /* status is not STATUS_OK => there's no attached element,
181    * and this is the last result message we get */
182   if (GNUNET_SET_STATUS_OK != result_status)
183   {
184     GNUNET_MQ_assoc_remove (set->mq, ntohl (msg->request_id));
185     GNUNET_CONTAINER_DLL_remove (oh->set->ops_head, oh->set->ops_tail, oh);
186     if (GNUNET_YES == oh->set->destroy_requested)
187       GNUNET_SET_destroy (oh->set);
188     if (NULL != oh->result_cb)
189       oh->result_cb (oh->result_cls, NULL, result_status);
190     GNUNET_free (oh);
191     return;
192   }
193
194   e.data = &msg[1];
195   e.size = ntohs (mh->size) - sizeof (struct GNUNET_SET_ResultMessage);
196   e.type = msg->element_type;
197   if (NULL != oh->result_cb)
198     oh->result_cb (oh->result_cls, &e, result_status);
199 }
200
201 /**
202  * Handle request message for a listen operation
203  *
204  * @param cls the listen handle
205  * @param mh the message
206  */
207 static void
208 handle_request (void *cls, const struct GNUNET_MessageHeader *mh)
209 {
210   struct GNUNET_SET_RequestMessage *msg = (struct GNUNET_SET_RequestMessage *) mh;
211   struct GNUNET_SET_ListenHandle *lh = cls;
212   struct GNUNET_SET_Request *req;
213   struct GNUNET_MessageHeader *context_msg;
214
215   LOG (GNUNET_ERROR_TYPE_DEBUG, "processing operation request\n");
216   req = GNUNET_new (struct GNUNET_SET_Request);
217   req->accept_id = ntohl (msg->accept_id);
218   context_msg = GNUNET_MQ_extract_nested_mh (msg);
219   /* calling GNUNET_SET_accept in the listen cb will set req->accepted */
220   lh->listen_cb (lh->listen_cls, &msg->peer_id, context_msg, req);
221
222   if (GNUNET_NO == req->accepted)
223   {
224     struct GNUNET_MQ_Envelope *mqm;
225     struct GNUNET_SET_AcceptRejectMessage *amsg;
226
227     mqm = GNUNET_MQ_msg (amsg, GNUNET_MESSAGE_TYPE_SET_REJECT);
228     /* no request id, as we refused */
229     amsg->request_id = htonl (0);
230     amsg->accept_reject_id = msg->accept_id;
231     GNUNET_MQ_send (lh->mq, mqm);
232     GNUNET_free (req);
233     LOG (GNUNET_ERROR_TYPE_DEBUG, "rejecting request\n");
234   }
235
236   LOG (GNUNET_ERROR_TYPE_DEBUG, "processed op request from service\n");
237
238   /* the accept-case is handled in GNUNET_SET_accept,
239    * as we have the accept message available there */
240 }
241
242
243 static void
244 handle_client_listener_error (void *cls, enum GNUNET_MQ_Error error)
245 {
246   struct GNUNET_SET_ListenHandle *lh = cls;
247
248   lh->listen_cb (lh->listen_cls, NULL, NULL, NULL);
249 }
250
251
252 static void
253 handle_client_set_error (void *cls, enum GNUNET_MQ_Error error)
254 {
255   struct GNUNET_SET_Handle *set = cls;
256
257   while (NULL != set->ops_head)
258   {
259     if (NULL != set->ops_head->result_cb)
260       set->ops_head->result_cb (set->ops_head->result_cls, NULL,
261                                 GNUNET_SET_STATUS_FAILURE);
262     GNUNET_SET_operation_cancel (set->ops_head);
263   }
264
265   /* FIXME: there should be a set error handler */
266 }
267
268
269 /**
270  * Create an empty set, supporting the specified operation.
271  *
272  * @param cfg configuration to use for connecting to the
273  *        set service
274  * @param op operation supported by the set
275  *        Note that the operation has to be specified
276  *        beforehand, as certain set operations need to maintain
277  *        data structures spefific to the operation
278  * @return a handle to the set
279  */
280 struct GNUNET_SET_Handle *
281 GNUNET_SET_create (const struct GNUNET_CONFIGURATION_Handle *cfg,
282                    enum GNUNET_SET_OperationType op)
283 {
284   struct GNUNET_SET_Handle *set;
285   struct GNUNET_MQ_Envelope *mqm;
286   struct GNUNET_SET_CreateMessage *msg;
287   static const struct GNUNET_MQ_MessageHandler mq_handlers[] = {
288     {handle_result, GNUNET_MESSAGE_TYPE_SET_RESULT, 0},
289     GNUNET_MQ_HANDLERS_END
290   };
291
292   set = GNUNET_new (struct GNUNET_SET_Handle);
293   set->client = GNUNET_CLIENT_connect ("set", cfg);
294   LOG (GNUNET_ERROR_TYPE_DEBUG, "set client created\n");
295   GNUNET_assert (NULL != set->client);
296   set->mq = GNUNET_MQ_queue_for_connection_client (set->client, mq_handlers,
297                                                    handle_client_set_error, set);
298   GNUNET_assert (NULL != set->mq);
299   mqm = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SET_CREATE);
300   msg->operation = htons (op);
301   GNUNET_MQ_send (set->mq, mqm);
302   return set;
303 }
304
305
306 /**
307  * Add an element to the given set.
308  * After the element has been added (in the sense of being
309  * transmitted to the set service), cont will be called.
310  * Calls to add_element can be queued
311  *
312  * @param set set to add element to
313  * @param element element to add to the set
314  * @param cont continuation called after the element has been added
315  * @param cont_cls closure for cont
316  */
317 void
318 GNUNET_SET_add_element (struct GNUNET_SET_Handle *set,
319                         const struct GNUNET_SET_Element *element,
320                         GNUNET_SET_Continuation cont,
321                         void *cont_cls)
322 {
323   struct GNUNET_MQ_Envelope *mqm;
324   struct GNUNET_SET_ElementMessage *msg;
325
326   mqm = GNUNET_MQ_msg_extra (msg, element->size, GNUNET_MESSAGE_TYPE_SET_ADD);
327   msg->element_type = element->type;
328   memcpy (&msg[1], element->data, element->size);
329   GNUNET_MQ_notify_sent (mqm, cont, cont_cls);
330   GNUNET_MQ_send (set->mq, mqm);
331 }
332
333
334 /**
335  * Remove an element to the given set.
336  * After the element has been removed (in the sense of the
337  * request being transmitted to the set service), cont will be called.
338  * Calls to remove_element can be queued
339  *
340  * @param set set to remove element from
341  * @param element element to remove from the set
342  * @param cont continuation called after the element has been removed
343  * @param cont_cls closure for cont
344  */
345 void
346 GNUNET_SET_remove_element (struct GNUNET_SET_Handle *set,
347                            const struct GNUNET_SET_Element *element,
348                            GNUNET_SET_Continuation cont,
349                            void *cont_cls)
350 {
351   struct GNUNET_MQ_Envelope *mqm;
352   struct GNUNET_SET_ElementMessage *msg;
353
354   mqm = GNUNET_MQ_msg_extra (msg, element->size, GNUNET_MESSAGE_TYPE_SET_REMOVE);
355   msg->element_type = element->type;
356   memcpy (&msg[1], element->data, element->size);
357   GNUNET_MQ_notify_sent (mqm, cont, cont_cls);
358   GNUNET_MQ_send (set->mq, mqm);
359 }
360
361
362 /**
363  * Destroy the set handle, and free all associated resources.
364  */
365 void
366 GNUNET_SET_destroy (struct GNUNET_SET_Handle *set)
367 {
368   if (NULL != set->ops_head)
369   {
370     set->destroy_requested = GNUNET_YES;
371     return;
372   }
373   GNUNET_CLIENT_disconnect (set->client);
374   set->client = NULL;
375   GNUNET_MQ_destroy (set->mq);
376   set->mq = NULL;
377 }
378
379
380 /**
381  * Prepare a set operation to be evaluated with another peer.
382  * The evaluation will not start until the client provides
383  * a local set with GNUNET_SET_commit.
384  *
385  * @param other_peer peer with the other set
386  * @param app_id hash for the application using the set
387  * @param context_msg additional information for the request
388  * @param salt salt used for the set operation; sometimes set operations
389  *        fail due to hash collisions, using a different salt for each operation
390  *        makes it harder for an attacker to exploit this
391  * @param result_mode specified how results will be returned,
392  *        see 'GNUNET_SET_ResultMode'.
393  * @param result_cb called on error or success
394  * @param result_cls closure for result_cb
395  * @return a handle to cancel the operation
396  */
397 struct GNUNET_SET_OperationHandle *
398 GNUNET_SET_prepare (const struct GNUNET_PeerIdentity *other_peer,
399                     const struct GNUNET_HashCode *app_id,
400                     const struct GNUNET_MessageHeader *context_msg,
401                     uint16_t salt,
402                     enum GNUNET_SET_ResultMode result_mode,
403                     GNUNET_SET_ResultIterator result_cb,
404                     void *result_cls)
405 {
406   struct GNUNET_MQ_Envelope *mqm;
407   struct GNUNET_SET_OperationHandle *oh;
408   struct GNUNET_SET_EvaluateMessage *msg;
409
410   oh = GNUNET_new (struct GNUNET_SET_OperationHandle);
411   oh->result_cb = result_cb;
412   oh->result_cls = result_cls;
413
414   mqm = GNUNET_MQ_msg_nested_mh (msg, GNUNET_MESSAGE_TYPE_SET_EVALUATE, context_msg);
415
416   msg->app_id = *app_id;
417   msg->target_peer = *other_peer;
418   msg->salt = salt;
419   msg->reserved = 0;
420   oh->conclude_mqm = mqm;
421   oh->request_id_addr = &msg->request_id;
422
423   return oh;
424 }
425
426 /**
427  * Wait for set operation requests for the given application id
428  * 
429  * @param cfg configuration to use for connecting to
430  *            the set service
431  * @param operation operation we want to listen for
432  * @param app_id id of the application that handles set operation requests
433  * @param listen_cb called for each incoming request matching the operation
434  *                  and application id
435  * @param listen_cls handle for listen_cb
436  * @return a handle that can be used to cancel the listen operation
437  */
438 struct GNUNET_SET_ListenHandle *
439 GNUNET_SET_listen (const struct GNUNET_CONFIGURATION_Handle *cfg,
440                    enum GNUNET_SET_OperationType operation,
441                    const struct GNUNET_HashCode *app_id,
442                    GNUNET_SET_ListenCallback listen_cb,
443                    void *listen_cls)
444 {
445   struct GNUNET_SET_ListenHandle *lh;
446   struct GNUNET_MQ_Envelope *mqm;
447   struct GNUNET_SET_ListenMessage *msg;
448   static const struct GNUNET_MQ_MessageHandler mq_handlers[] = {
449     {handle_request, GNUNET_MESSAGE_TYPE_SET_REQUEST},
450     GNUNET_MQ_HANDLERS_END
451   };
452
453   lh = GNUNET_new (struct GNUNET_SET_ListenHandle);
454   lh->client = GNUNET_CLIENT_connect ("set", cfg);
455   lh->listen_cb = listen_cb;
456   lh->listen_cls = listen_cls;
457   GNUNET_assert (NULL != lh->client);
458   lh->mq = GNUNET_MQ_queue_for_connection_client (lh->client, mq_handlers,
459                                                   handle_client_listener_error, lh);
460   mqm = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SET_LISTEN);
461   msg->operation = htons (operation);
462   msg->app_id = *app_id;
463   GNUNET_MQ_send (lh->mq, mqm);
464
465   return lh;
466 }
467
468
469 /**
470  * Cancel the given listen operation.
471  *
472  * @param lh handle for the listen operation
473  */
474 void
475 GNUNET_SET_listen_cancel (struct GNUNET_SET_ListenHandle *lh)
476 {
477   GNUNET_CLIENT_disconnect (lh->client);
478   GNUNET_MQ_destroy (lh->mq);
479   GNUNET_free (lh);
480 }
481
482
483 /**
484  * Accept a request we got via GNUNET_SET_listen.  Must be called during
485  * GNUNET_SET_listen, as the 'struct GNUNET_SET_Request' becomes invalid
486  * afterwards.
487  * Call GNUNET_SET_conclude to provide the local set to use for the operation,
488  * and to begin the exchange with the remote peer. 
489  *
490  * @param request request to accept
491  * @param result_mode specified how results will be returned,
492  *        see 'GNUNET_SET_ResultMode'.
493  * @param result_cb callback for the results
494  * @param result_cls closure for result_cb
495  * @return a handle to cancel the operation
496  */
497 struct GNUNET_SET_OperationHandle *
498 GNUNET_SET_accept (struct GNUNET_SET_Request *request,
499                    enum GNUNET_SET_ResultMode result_mode,
500                    GNUNET_SET_ResultIterator result_cb,
501                    void *result_cls)
502 {
503   struct GNUNET_MQ_Envelope *mqm;
504   struct GNUNET_SET_OperationHandle *oh;
505   struct GNUNET_SET_AcceptRejectMessage *msg;
506
507   GNUNET_assert (NULL != request);
508   GNUNET_assert (GNUNET_NO == request->accepted);
509   request->accepted = GNUNET_YES;
510
511   oh = GNUNET_new (struct GNUNET_SET_OperationHandle);
512   oh->result_cb = result_cb;
513   oh->result_cls = result_cls;
514
515   mqm = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SET_ACCEPT);
516   msg->accept_reject_id = htonl (request->accept_id);
517
518   oh->conclude_mqm = mqm;
519   oh->request_id_addr = &msg->request_id;
520
521   return oh;
522 }
523
524
525 /**
526  * Cancel the given set operation.
527  * We need to send an explicit cancel message, as
528  * all operations communicate with the set's client
529  * handle.
530  *
531  * @param oh set operation to cancel
532  */
533 void
534 GNUNET_SET_operation_cancel (struct GNUNET_SET_OperationHandle *oh)
535 {
536   struct GNUNET_MQ_Envelope *mqm;
537   struct GNUNET_SET_OperationHandle *h_assoc;
538
539   GNUNET_assert (NULL != oh->set);
540
541   GNUNET_CONTAINER_DLL_remove (oh->set->ops_head, oh->set->ops_tail, oh);
542   h_assoc = GNUNET_MQ_assoc_remove (oh->set->mq, oh->request_id);
543   GNUNET_assert (h_assoc == oh);
544   mqm = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_CANCEL);
545   GNUNET_MQ_send (oh->set->mq, mqm);
546
547   if (NULL != oh->conclude_mqm)
548     GNUNET_MQ_discard (oh->conclude_mqm);
549
550   if (GNUNET_YES == oh->set->destroy_requested)
551     GNUNET_SET_destroy (oh->set);
552
553   GNUNET_free (oh);
554 }
555
556
557 /**
558  * Commit a set to be used with a set operation.
559  * This function is called once we have fully constructed
560  * the set that we want to use for the operation.  At this
561  * time, the P2P protocol can then begin to exchange the
562  * set information and call the result callback with the
563  * result information.
564  *
565  * @param oh handle to the set operation 
566  * @param set the set to use for the operation
567  */
568 void
569 GNUNET_SET_commit (struct GNUNET_SET_OperationHandle *oh,
570                    struct GNUNET_SET_Handle *set)
571 {
572   GNUNET_assert (NULL == oh->set);
573   GNUNET_assert (NULL != oh->conclude_mqm);
574   oh->set = set;
575   GNUNET_CONTAINER_DLL_insert (set->ops_head, set->ops_tail, oh);
576   oh->request_id = GNUNET_MQ_assoc_add (set->mq, oh);
577   *oh->request_id_addr = htonl (oh->request_id);
578   GNUNET_MQ_send (set->mq, oh->conclude_mqm);
579   oh->conclude_mqm = NULL;
580   oh->request_id_addr = NULL;
581 }
582