e1b6132cb26cbffc0b77326afa9f8b4ba6b5c978
[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 /**
38  * Opaque handle to a set.
39  */
40 struct GNUNET_SET_Handle
41 {
42   struct GNUNET_CLIENT_Connection *client;
43   struct GNUNET_MQ_Handle *mq;
44   unsigned int messages_since_ack;
45 };
46
47 /**
48  * Opaque handle to a set operation request from another peer.
49  */
50 struct GNUNET_SET_Request
51 {
52   uint32_t accept_id;
53   int accepted;
54 };
55
56 struct GNUNET_SET_OperationHandle
57 {
58   GNUNET_SET_ResultIterator result_cb;
59   void *result_cls;
60
61   /**
62    * Local set used for the operation,
63    * NULL if no set has been provided by conclude yet.
64    */
65   struct GNUNET_SET_Handle *set;
66
67   /**
68    * Request ID to identify the operation within the set.
69    */
70   uint32_t request_id;
71
72   /**
73    * Message sent to the server on calling conclude,
74    * NULL if conclude has been called.
75    */
76   struct GNUNET_MQ_Envelope *conclude_mqm;
77
78   /**
79    * Address of the request if in the conclude message,
80    * used to patch the request id into the message when the set is known.
81    */
82   uint32_t *request_id_addr;
83 };
84
85
86 /**
87  * Opaque handle to a listen operation.
88  */
89 struct GNUNET_SET_ListenHandle
90 {
91   struct GNUNET_CLIENT_Connection *client;
92   struct GNUNET_MQ_Handle* mq;
93   GNUNET_SET_ListenCallback listen_cb;
94   void *listen_cls;
95 };
96
97
98 /**
99  * Handle result message for a set operation.
100  *
101  * @param cls the set
102  * @param mh the message
103  */
104 static void
105 handle_result (void *cls, const struct GNUNET_MessageHeader *mh)
106 {
107   struct GNUNET_SET_ResultMessage *msg = (struct GNUNET_SET_ResultMessage *) mh;
108   struct GNUNET_SET_Handle *set = cls;
109   struct GNUNET_SET_OperationHandle *oh;
110   struct GNUNET_SET_Element e;
111
112
113   GNUNET_assert (NULL != set);
114   GNUNET_assert (NULL != set->mq);
115
116   if (set->messages_since_ack >= GNUNET_SET_ACK_WINDOW/2)
117   {
118     struct GNUNET_MQ_Envelope *mqm;
119     mqm = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_ACK);
120     GNUNET_MQ_send (set->mq, mqm);
121   }
122   oh = GNUNET_MQ_assoc_get (set->mq, ntohl (msg->request_id));
123   GNUNET_assert (NULL != oh);
124   /* status is not STATUS_OK => there's no attached element,
125    * and this is the last result message we get */
126   if (htons (msg->result_status) != GNUNET_SET_STATUS_OK)
127   {
128     GNUNET_MQ_assoc_remove (set->mq, ntohl (msg->request_id));
129     if (NULL != oh->result_cb)
130       oh->result_cb (oh->result_cls, NULL, htons (msg->result_status));
131     GNUNET_free (oh);
132     return;
133   }
134
135   e.data = &msg[1];
136   e.size = ntohs (mh->size) - sizeof (struct GNUNET_SET_ResultMessage);
137   e.type = msg->element_type;
138   if (NULL != oh->result_cb)
139     oh->result_cb (oh->result_cls, &e, htons (msg->result_status));
140 }
141
142 /**
143  * Handle request message for a listen operation
144  *
145  * @param cls the listen handle
146  * @param mh the message
147  */
148 static void
149 handle_request (void *cls, const struct GNUNET_MessageHeader *mh)
150 {
151   struct GNUNET_SET_RequestMessage *msg = (struct GNUNET_SET_RequestMessage *) mh;
152   struct GNUNET_SET_ListenHandle *lh = cls;
153   struct GNUNET_SET_Request *req;
154   struct GNUNET_MessageHeader *context_msg;
155
156   LOG (GNUNET_ERROR_TYPE_INFO, "processing request\n");
157   req = GNUNET_new (struct GNUNET_SET_Request);
158   req->accept_id = ntohl (msg->accept_id);
159   context_msg = GNUNET_MQ_extract_nested_mh (msg);
160   /* calling GNUNET_SET_accept in the listen cb will set req->accepted */
161   lh->listen_cb (lh->listen_cls, &msg->peer_id, context_msg, req);
162
163   if (GNUNET_NO == req->accepted)
164   {
165     struct GNUNET_MQ_Envelope *mqm;
166     struct GNUNET_SET_AcceptRejectMessage *amsg;
167
168     mqm = GNUNET_MQ_msg (amsg, GNUNET_MESSAGE_TYPE_SET_REJECT);
169     /* no request id, as we refused */
170     amsg->request_id = htonl (0);
171     amsg->accept_reject_id = msg->accept_id;
172     GNUNET_MQ_send (lh->mq, mqm);
173     GNUNET_free (req);
174     LOG (GNUNET_ERROR_TYPE_INFO, "rejecting request\n");
175   }
176
177   LOG (GNUNET_ERROR_TYPE_INFO, "processed op request from service\n");
178
179   /* the accept-case is handled in GNUNET_SET_accept,
180    * as we have the accept message available there */
181 }
182
183
184 /**
185  * Create an empty set, supporting the specified operation.
186  *
187  * @param cfg configuration to use for connecting to the
188  *        set service
189  * @param op operation supported by the set
190  *        Note that the operation has to be specified
191  *        beforehand, as certain set operations need to maintain
192  *        data structures spefific to the operation
193  * @return a handle to the set
194  */
195 struct GNUNET_SET_Handle *
196 GNUNET_SET_create (const struct GNUNET_CONFIGURATION_Handle *cfg,
197                    enum GNUNET_SET_OperationType op)
198 {
199   struct GNUNET_SET_Handle *set;
200   struct GNUNET_MQ_Envelope *mqm;
201   struct GNUNET_SET_CreateMessage *msg;
202   static const struct GNUNET_MQ_MessageHandler mq_handlers[] = {
203     {handle_result, GNUNET_MESSAGE_TYPE_SET_RESULT},
204     GNUNET_MQ_HANDLERS_END
205   };
206
207   set = GNUNET_new (struct GNUNET_SET_Handle);
208   set->client = GNUNET_CLIENT_connect ("set", cfg);
209   LOG (GNUNET_ERROR_TYPE_INFO, "set client created\n");
210   GNUNET_assert (NULL != set->client);
211   set->mq = GNUNET_MQ_queue_for_connection_client (set->client, mq_handlers, set);
212   GNUNET_assert (NULL != set->mq);
213   mqm = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SET_CREATE);
214   msg->operation = htons (op);
215   GNUNET_MQ_send (set->mq, mqm);
216   return set;
217 }
218
219
220 /**
221  * Add an element to the given set.
222  * After the element has been added (in the sense of being
223  * transmitted to the set service), cont will be called.
224  * Calls to add_element can be queued
225  *
226  * @param set set to add element to
227  * @param element element to add to the set
228  * @param cont continuation called after the element has been added
229  * @param cont_cls closure for cont
230  */
231 void
232 GNUNET_SET_add_element (struct GNUNET_SET_Handle *set,
233                         const struct GNUNET_SET_Element *element,
234                         GNUNET_SET_Continuation cont,
235                         void *cont_cls)
236 {
237   struct GNUNET_MQ_Envelope *mqm;
238   struct GNUNET_SET_ElementMessage *msg;
239
240   mqm = GNUNET_MQ_msg_extra (msg, element->size, GNUNET_MESSAGE_TYPE_SET_ADD);
241   msg->element_type = element->type;
242   memcpy (&msg[1], element->data, element->size);
243   GNUNET_MQ_notify_sent (mqm, cont, cont_cls);
244   GNUNET_MQ_send (set->mq, mqm);
245 }
246
247
248 /**
249  * Remove an element to the given set.
250  * After the element has been removed (in the sense of the
251  * request being transmitted to the set service), cont will be called.
252  * Calls to remove_element can be queued
253  *
254  * @param set set to remove element from
255  * @param element element to remove from the set
256  * @param cont continuation called after the element has been removed
257  * @param cont_cls closure for cont
258  */
259 void
260 GNUNET_SET_remove_element (struct GNUNET_SET_Handle *set,
261                            const struct GNUNET_SET_Element *element,
262                            GNUNET_SET_Continuation cont,
263                            void *cont_cls)
264 {
265   struct GNUNET_MQ_Envelope *mqm;
266   struct GNUNET_SET_ElementMessage *msg;
267
268   mqm = GNUNET_MQ_msg_extra (msg, element->size, GNUNET_MESSAGE_TYPE_SET_REMOVE);
269   msg->element_type = element->type;
270   memcpy (&msg[1], element->data, element->size);
271   GNUNET_MQ_notify_sent (mqm, cont, cont_cls);
272   GNUNET_MQ_send (set->mq, mqm);
273 }
274
275
276 /**
277  * Destroy the set handle, and free all associated resources.
278  */
279 void
280 GNUNET_SET_destroy (struct GNUNET_SET_Handle *set)
281 {
282   GNUNET_CLIENT_disconnect (set->client);
283   set->client = NULL;
284   GNUNET_MQ_destroy (set->mq);
285   set->mq = NULL;
286 }
287
288
289 /**
290  * Prepare a set operation to be evaluated with another peer.
291  * The evaluation will not start until the client provides
292  * a local set with GNUNET_SET_commit.
293  *
294  * @param other_peer peer with the other set
295  * @param app_id hash for the application using the set
296  * @param context_msg additional information for the request
297  * @param salt salt used for the set operation; sometimes set operations
298  *        fail due to hash collisions, using a different salt for each operation
299  *        makes it harder for an attacker to exploit this
300  * @param result_mode specified how results will be returned,
301  *        see 'GNUNET_SET_ResultMode'.
302  * @param result_cb called on error or success
303  * @param result_cls closure for result_cb
304  * @return a handle to cancel the operation
305  */
306 struct GNUNET_SET_OperationHandle *
307 GNUNET_SET_prepare (const struct GNUNET_PeerIdentity *other_peer,
308                     const struct GNUNET_HashCode *app_id,
309                     const struct GNUNET_MessageHeader *context_msg,
310                     uint16_t salt,
311                     enum GNUNET_SET_ResultMode result_mode,
312                     GNUNET_SET_ResultIterator result_cb,
313                     void *result_cls)
314 {
315   struct GNUNET_MQ_Envelope *mqm;
316   struct GNUNET_SET_OperationHandle *oh;
317   struct GNUNET_SET_EvaluateMessage *msg;
318
319   oh = GNUNET_new (struct GNUNET_SET_OperationHandle);
320   oh->result_cb = result_cb;
321   oh->result_cls = result_cls;
322
323   mqm = GNUNET_MQ_msg_nested_mh (msg, GNUNET_MESSAGE_TYPE_SET_EVALUATE, context_msg);
324
325   msg->app_id = *app_id;
326   msg->target_peer = *other_peer;
327   msg->salt = salt;
328   msg->reserved = 0;
329   oh->conclude_mqm = mqm;
330   oh->request_id_addr = &msg->request_id;
331
332   return oh;
333 }
334
335
336 /**
337  * Wait for set operation requests for the given application id
338  * 
339  * @param cfg configuration to use for connecting to
340  *            the set service
341  * @param operation operation we want to listen for
342  * @param app_id id of the application that handles set operation requests
343  * @param listen_cb called for each incoming request matching the operation
344  *                  and application id
345  * @param listen_cls handle for listen_cb
346  * @return a handle that can be used to cancel the listen operation
347  */
348 struct GNUNET_SET_ListenHandle *
349 GNUNET_SET_listen (const struct GNUNET_CONFIGURATION_Handle *cfg,
350                    enum GNUNET_SET_OperationType operation,
351                    const struct GNUNET_HashCode *app_id,
352                    GNUNET_SET_ListenCallback listen_cb,
353                    void *listen_cls)
354 {
355   struct GNUNET_SET_ListenHandle *lh;
356   struct GNUNET_MQ_Envelope *mqm;
357   struct GNUNET_SET_ListenMessage *msg;
358   static const struct GNUNET_MQ_MessageHandler mq_handlers[] = {
359     {handle_request, GNUNET_MESSAGE_TYPE_SET_REQUEST},
360     GNUNET_MQ_HANDLERS_END
361   };
362
363   lh = GNUNET_new (struct GNUNET_SET_ListenHandle);
364   lh->client = GNUNET_CLIENT_connect ("set", cfg);
365   lh->listen_cb = listen_cb;
366   lh->listen_cls = listen_cls;
367   GNUNET_assert (NULL != lh->client);
368   lh->mq = GNUNET_MQ_queue_for_connection_client (lh->client, mq_handlers, lh);
369   mqm = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SET_LISTEN);
370   msg->operation = htons (operation);
371   msg->app_id = *app_id;
372   GNUNET_MQ_send (lh->mq, mqm);
373
374   return lh;
375 }
376
377
378 /**
379  * Cancel the given listen operation.
380  *
381  * @param lh handle for the listen operation
382  */
383 void
384 GNUNET_SET_listen_cancel (struct GNUNET_SET_ListenHandle *lh)
385 {
386   GNUNET_CLIENT_disconnect (lh->client);
387   GNUNET_MQ_destroy (lh->mq);
388   GNUNET_free (lh);
389 }
390
391
392 /**
393  * Accept a request we got via GNUNET_SET_listen.  Must be called during
394  * GNUNET_SET_listen, as the 'struct GNUNET_SET_Request' becomes invalid
395  * afterwards.
396  * Call GNUNET_SET_conclude to provide the local set to use for the operation,
397  * and to begin the exchange with the remote peer. 
398  *
399  * @param request request to accept
400  * @param result_mode specified how results will be returned,
401  *        see 'GNUNET_SET_ResultMode'.
402  * @param result_cb callback for the results
403  * @param cls closure for result_cb
404  * @return a handle to cancel the operation
405  */
406 struct GNUNET_SET_OperationHandle *
407 GNUNET_SET_accept (struct GNUNET_SET_Request *request,
408                    enum GNUNET_SET_ResultMode result_mode,
409                    GNUNET_SET_ResultIterator result_cb,
410                    void *cls)
411 {
412   struct GNUNET_MQ_Envelope *mqm;
413   struct GNUNET_SET_OperationHandle *oh;
414   struct GNUNET_SET_AcceptRejectMessage *msg;
415
416   GNUNET_assert (GNUNET_NO == request->accepted);
417   request->accepted = GNUNET_YES;
418
419   oh = GNUNET_new (struct GNUNET_SET_OperationHandle);
420   oh->result_cb = result_cb;
421   oh->result_cls = cls;
422
423   mqm = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SET_ACCEPT);
424   msg->accept_reject_id = htonl (request->accept_id);
425
426   oh->conclude_mqm = mqm;
427   oh->request_id_addr = &msg->request_id;
428
429   return oh;
430 }
431
432
433 /**
434  * Cancel the given set operation.
435  *
436  * @param oh set operation to cancel
437  */
438 void
439 GNUNET_SET_operation_cancel (struct GNUNET_SET_OperationHandle *oh)
440 {
441   struct GNUNET_MQ_Envelope *mqm;
442   struct GNUNET_SET_OperationHandle *h_assoc;
443
444   if (NULL != oh->set)
445   {
446     h_assoc = GNUNET_MQ_assoc_remove (oh->set->mq, oh->request_id);
447     GNUNET_assert (h_assoc == oh);
448     mqm = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_CANCEL);
449     GNUNET_MQ_send (oh->set->mq, mqm);
450   }
451
452   if (NULL != oh->conclude_mqm)
453     GNUNET_MQ_discard (oh->conclude_mqm);
454
455   GNUNET_free (oh);
456 }
457
458
459 /**
460  * Commit a set to be used with a set operation.
461  * This function is called once we have fully constructed
462  * the set that we want to use for the operation.  At this
463  * time, the P2P protocol can then begin to exchange the
464  * set information and call the result callback with the
465  * result information.
466  *
467  * @param oh handle to the set operation 
468  * @param set the set to use for the operation
469  */
470 void
471 GNUNET_SET_commit (struct GNUNET_SET_OperationHandle *oh,
472                      struct GNUNET_SET_Handle *set)
473 {
474   GNUNET_assert (NULL == oh->set);
475   GNUNET_assert (NULL != oh->conclude_mqm);
476   oh->set = set;
477   oh->request_id = GNUNET_MQ_assoc_add (oh->set->mq, oh);
478   *oh->request_id_addr = htonl (oh->request_id);
479   GNUNET_MQ_send (oh->set->mq, oh->conclude_mqm);
480   oh->conclude_mqm = NULL;
481   oh->request_id_addr = NULL;
482 }
483