-comments
[oweals/gnunet.git] / src / set / gnunet-service-set.c
1 /*
2       This file is part of GNUnet
3       (C) 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 2, 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/gnunet-service-set.c
23  * @brief two-peer set operations
24  * @author Florian Dold
25  */
26
27
28 #include "gnunet-service-set.h"
29 #include "set_protocol.h"
30
31
32 /**
33  * Configuration of our local peer.
34  */
35 const struct GNUNET_CONFIGURATION_Handle *configuration;
36
37 /**
38  * Socket listening for other peers via stream.
39  */
40 static struct GNUNET_STREAM_ListenSocket *stream_listen_socket;
41
42 /**
43  * Sets are held in a doubly linked list.
44  */
45 static struct Set *sets_head;
46
47 /**
48  * Sets are held in a doubly linked list.
49  */
50 static struct Set *sets_tail;
51
52 /**
53  * Listeners are held in a doubly linked list.
54  */
55 static struct Listener *listeners_head;
56
57 /**
58  * Listeners are held in a doubly linked list.
59  */
60 static struct Listener *listeners_tail;
61
62 /**
63  * Incoming sockets from remote peers are
64  * held in a doubly linked list.
65  */
66 static struct Incoming *incoming_head;
67
68 /**
69  * Incoming sockets from remote peers are
70  * held in a doubly linked list.
71  */
72 static struct Incoming *incoming_tail;
73
74 /**
75  * Counter for allocating unique request IDs for clients.
76  */
77 static uint32_t request_id = 1;
78
79
80 /**
81  * Disconnect a client and free all resources
82  * that the client allocated (e.g. Sets or Listeners)
83  *
84  * @param client the client to disconnect
85  */
86 void
87 client_disconnect (struct GNUNET_SERVER_Client *client)
88 {
89   /* FIXME: clean up any data structures belonging to the client */
90   GNUNET_SERVER_client_disconnect (client);
91 }
92
93
94 /**
95  * Get set that is owned by the client, if any.
96  *
97  * @param client client to look for
98  * @return set that the client owns, NULL if the client
99  *         does not own a set
100  */
101 static struct Set *
102 get_set (struct GNUNET_SERVER_Client *client)
103 {
104   struct Set *set;
105   for (set = sets_head; NULL != set; set = set->next)
106     if (set->client == client)
107       return set;
108   return NULL;
109 }
110
111
112 /**
113  * Get the listener associated to a client, if any.
114  *
115  * @param client the client
116  * @return listener associated with the client, NULL
117  *         if there isn't any
118  */
119 static struct Listener *
120 get_listener (struct GNUNET_SERVER_Client *client)
121 {
122   struct Listener *listener;
123   for (listener = listeners_head; NULL != listener; listener = listener->next)
124     if (listener->client == client)
125       return listener;
126   return NULL;
127 }
128
129
130 /**
131  * Get the incoming socket associated with the given id
132  *
133  * @param id id to look for
134  * @return the incoming socket associated with the id,
135  *         or NULL if there is none
136  */
137 static struct Incoming *
138 get_incoming (uint32_t id)
139 {
140   struct Incoming *incoming;
141   for (incoming = incoming_head; NULL != incoming; incoming = incoming->next)
142     if (incoming->request_id == id)
143       return incoming;
144   return NULL;
145 }
146
147
148 static void
149 destroy_incoming (struct Incoming *incoming)
150 {
151   if (NULL != incoming->mq)
152   {
153     GNUNET_MQ_destroy (incoming->mq);
154     incoming->mq = NULL;
155   }
156   if (NULL != incoming->socket)
157   {
158     GNUNET_STREAM_close (incoming->socket);
159     incoming->socket = NULL;
160   }
161   GNUNET_CONTAINER_DLL_remove (incoming_head, incoming_tail, incoming);
162   GNUNET_free (incoming);
163 }
164
165
166 /**
167  * Handle a request for a set operation for
168  * another peer.
169  *
170  * @param cls the incoming socket
171  * @param mh the message
172  */
173 static void
174 handle_p2p_operation_request (void *cls, const struct GNUNET_MessageHeader *mh)
175 {
176   struct Incoming *incoming = cls;
177   const struct OperationRequestMessage *msg = (const struct OperationRequestMessage *) mh;
178   struct GNUNET_MQ_Message *mqm;
179   struct RequestMessage *cmsg;
180   struct Listener *listener;
181   const struct GNUNET_MessageHeader *context_msg;
182
183   if (ntohs (mh->size) < sizeof *msg)
184   {
185     GNUNET_break (0);
186     destroy_incoming (incoming);
187     return;
188   }
189   else if (ntohs (mh->size) == sizeof *msg)
190   {
191     context_msg = NULL;
192   }
193   else
194   {
195     context_msg = &msg[1].header;
196     if ((ntohs (context_msg->size) + sizeof *msg) != ntohs (msg->header.size))
197     {
198       /* size of context message is invalid */
199       GNUNET_break (0);
200       destroy_incoming (incoming);
201       return;
202     }
203   }
204
205   for (listener = listeners_head; listener != NULL; listener = listener->next)
206   {
207     if ( (0 != GNUNET_CRYPTO_hash_cmp (&msg->app_id, &listener->app_id)) ||
208          (htons (msg->operation) != listener->operation) )
209       continue;
210     mqm = GNUNET_MQ_msg_concat (cmsg, context_msg, GNUNET_MESSAGE_TYPE_SET_REQUEST);
211     incoming->request_id = request_id++;
212     cmsg->request_id = htonl (incoming->request_id);
213     GNUNET_MQ_send (listener->client_mq, mqm);
214     return;
215   }
216 }
217
218
219 /**
220  * Called when a client wants to create a new set.
221  *
222  * @param cls unused
223  * @param client client that sent the message
224  * @param m message sent by the client
225  */
226 static void
227 handle_client_create (void *cls,
228                       struct GNUNET_SERVER_Client *client,
229                       const struct GNUNET_MessageHeader *m)
230 {
231   struct SetCreateMessage *msg = (struct SetCreateMessage *) m;
232   struct Set *set;
233
234   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "new set created\n");
235
236   if (NULL != get_set (client))
237   {
238     GNUNET_break (0);
239     GNUNET_SERVER_client_disconnect (client);
240     return;
241   }
242
243   set = GNUNET_new (struct Set);
244
245   switch (ntohs (msg->operation))
246   {
247     case GNUNET_SET_OPERATION_INTERSECTION:
248       /* FIXME: cfuchs */
249       GNUNET_assert (0);
250       break;
251     case GNUNET_SET_OPERATION_UNION:
252       set = union_set_create ();
253       break;
254     default:
255       GNUNET_free (set);
256       GNUNET_break (0);
257       GNUNET_SERVER_client_disconnect (client);
258       return;
259   }
260
261   set->client = client;
262   set->client_mq = GNUNET_MQ_queue_for_server_client (client);
263   GNUNET_CONTAINER_DLL_insert (sets_head, sets_tail, set);
264
265   GNUNET_SERVER_receive_done (client, GNUNET_OK);
266 }
267
268
269 /**
270  * Called when a client wants to create a new set.
271  *
272  * @param cls unused
273  * @param client client that sent the message
274  * @param m message sent by the client
275  */
276 static void
277 handle_client_listen (void *cls,
278                       struct GNUNET_SERVER_Client *client,
279                       const struct GNUNET_MessageHeader *m)
280 {
281   struct ListenMessage *msg = (struct ListenMessage *) m;
282   struct Listener *listener;
283   
284   if (NULL != get_listener (client))
285   {
286     GNUNET_break (0);
287     GNUNET_SERVER_client_disconnect (client);
288     return;
289   }
290   
291   listener = GNUNET_new (struct Listener);
292   listener->app_id = msg->app_id;
293   listener->operation = msg->operation;
294   GNUNET_CONTAINER_DLL_insert_tail (listeners_head, listeners_tail, listener);
295 }
296
297
298 /**
299  * Called when a client wants to create a new set.
300  *
301  * @param cls unused
302  * @param client client that sent the message
303  * @param m message sent by the client
304  */
305 static void
306 handle_client_add (void *cls,
307                       struct GNUNET_SERVER_Client *client,
308                       const struct GNUNET_MessageHeader *m)
309 {
310   struct Set *set;
311
312   set = get_set (client);
313   if (NULL == set)
314   {
315     GNUNET_break (0);
316     client_disconnect (client);
317     return;
318   }
319   switch (set->operation)
320   {
321     case GNUNET_SET_OPERATION_UNION:
322       union_add (set, (struct ElementMessage *) m);
323       break;
324     case GNUNET_SET_OPERATION_INTERSECTION:
325       /* FIXME: cfuchs */
326       break;
327     default:
328       GNUNET_assert (0);
329       break;
330   }
331 }
332
333
334 /**
335  * Called when a client wants to evaluate a set operation with another peer.
336  *
337  * @param cls unused
338  * @param client client that sent the message
339  * @param m message sent by the client
340  */
341 static void
342 handle_client_evaluate (void *cls,
343                         struct GNUNET_SERVER_Client *client,
344                         const struct GNUNET_MessageHeader *m)
345 {
346   struct Set *set;
347   struct EvaluateMessage *msg = (struct EvaluateMessage *) m;
348   struct EvaluateOperation *eo;
349
350   set = get_set (client);
351
352   if (NULL == set)
353   {
354     GNUNET_break (0);
355     client_disconnect (client);
356     return;
357   }
358
359   eo = GNUNET_new (struct EvaluateOperation);
360   eo->peer = msg->peer;
361   eo->app_id = msg->app_id;
362   eo->request_id = msg->request_id;
363   eo->context_msg = GNUNET_copy_message (&msg[1].header);
364   eo->set = set;
365
366   switch (set->operation)
367   {
368     case GNUNET_SET_OPERATION_INTERSECTION:
369       /* FIXME: cfuchs */
370       break;
371     case GNUNET_SET_OPERATION_UNION:
372       union_evaluate (eo);
373       break;
374     default:
375       GNUNET_assert (0);
376       break;
377   }
378 }
379
380
381 /**
382  * Handle a cancel request from a client.
383  *
384  * @param cls unused
385  * @param client the client
386  * @param m the cancel message
387  */
388 static void
389 handle_client_cancel (void *cls,
390                       struct GNUNET_SERVER_Client *client,
391                       const struct GNUNET_MessageHeader *m)
392 {
393   /* FIXME: implement */
394 }
395
396
397 /**
398  * Handle an ack from a client.
399  *
400  * @param cls unused
401  * @param client the client
402  * @param m the message
403  */
404 static void
405 handle_client_ack (void *cls,
406                    struct GNUNET_SERVER_Client *client,
407                    const struct GNUNET_MessageHeader *m)
408 {
409   /* FIXME: implement */
410 }
411
412
413 /**
414  * Handle a request from the client to accept
415  * a set operation.
416  *
417  * @param cls unused
418  * @param client the client
419  * @param m the message
420  */
421 static void
422 handle_client_accept (void *cls,
423                       struct GNUNET_SERVER_Client *client,
424                       const struct GNUNET_MessageHeader *m)
425 {
426   struct AcceptMessage *msg = (struct AcceptMessage *) m;
427   struct Set *set;
428   struct Incoming *incoming;
429   struct EvaluateOperation *eo;
430
431   set = get_set (client);
432
433   if (NULL == set)
434   {
435     GNUNET_break (0);
436     client_disconnect (client);
437     return;
438   }
439
440   incoming = get_incoming (ntohl (msg->request_id));
441
442   if ( (NULL == incoming) ||
443        (incoming->operation != set->operation) )
444   {
445     GNUNET_break (0);
446     client_disconnect (client);
447     return;
448   }
449
450   eo = GNUNET_new (struct EvaluateOperation);
451   eo->peer = incoming->peer;
452   eo->app_id = incoming->app_id;
453   eo->request_id = msg->request_id;
454   eo->set = set;
455
456   switch (set->operation)
457   {
458     case GNUNET_SET_OPERATION_INTERSECTION:
459       /* FIXME: cfuchs*/
460       GNUNET_assert (0);
461       break;
462     case GNUNET_SET_OPERATION_UNION:
463       union_accept (eo, incoming);
464       break;
465     default:
466       GNUNET_assert (0);
467       break;
468   }
469 }
470
471
472 /**
473  * Functions of this type are called upon new stream connection from other peers
474  * or upon binding error which happen when the app_port given in
475  * GNUNET_STREAM_listen() is already taken.
476  *
477  * @param cls the closure from GNUNET_STREAM_listen
478  * @param socket the socket representing the stream; NULL on binding error
479  * @param initiator the identity of the peer who wants to establish a stream
480  *            with us; NULL on binding error
481  * @return GNUNET_OK to keep the socket open, GNUNET_SYSERR to close the
482  *             stream (the socket will be invalid after the call)
483  */
484 static int
485 stream_listen_cb (void *cls,
486                   struct GNUNET_STREAM_Socket *socket,
487                   const struct GNUNET_PeerIdentity *initiator)
488 {
489   struct Incoming *incoming;
490   static const struct GNUNET_MQ_Handler handlers[] = {
491     {handle_p2p_operation_request, GNUNET_MESSAGE_TYPE_SET_P2P_OPERATION_REQUEST},
492     GNUNET_MQ_HANDLERS_END
493   };
494
495   if (NULL == socket)
496   {
497     GNUNET_break (0);
498     return GNUNET_SYSERR;
499   }
500
501   incoming = GNUNET_new (struct Incoming);
502   incoming->peer = *initiator;
503   incoming->socket = socket;
504   incoming->mq = GNUNET_MQ_queue_for_stream_socket (incoming->socket, handlers, incoming);
505   /* FIXME: timeout for peers that only connect but don't send anything */
506   GNUNET_CONTAINER_DLL_insert_tail (incoming_head, incoming_tail, incoming);
507   return GNUNET_OK;
508 }
509
510
511 /**
512  * Called to clean up, after a shutdown has been requested.
513  *
514  * @param cls closure
515  * @param tc context information (why was this task triggered now)
516  */
517 static void
518 shutdown_task (void *cls,
519                const struct GNUNET_SCHEDULER_TaskContext *tc)
520 {
521   if (NULL != stream_listen_socket)
522   {
523     GNUNET_STREAM_listen_close (stream_listen_socket);
524     stream_listen_socket = NULL;
525   } 
526
527   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "handled shutdown request\n");
528 }
529
530
531 /**
532  * Function called by the service's run
533  * method to run service-specific setup code.
534  *
535  * @param cls closure
536  * @param server the initialized server
537  * @param cfg configuration to use
538  */
539 static void
540 run (void *cls, struct GNUNET_SERVER_Handle *server, const struct GNUNET_CONFIGURATION_Handle *cfg)
541 {
542   static const struct GNUNET_SERVER_MessageHandler server_handlers[] = {
543     {handle_client_create, NULL, GNUNET_MESSAGE_TYPE_SET_CREATE, 0},
544     {handle_client_listen, NULL, GNUNET_MESSAGE_TYPE_SET_LISTEN, 0},
545     {handle_client_add, NULL, GNUNET_MESSAGE_TYPE_SET_ADD, 0},
546     {handle_client_cancel, NULL, GNUNET_MESSAGE_TYPE_SET_CANCEL, 0},
547     {handle_client_evaluate, NULL, GNUNET_MESSAGE_TYPE_SET_EVALUATE, 0},
548     {handle_client_ack, NULL, GNUNET_MESSAGE_TYPE_SET_ACK, 0},
549     {handle_client_accept, NULL, GNUNET_MESSAGE_TYPE_SET_ACCEPT, 0},
550     {NULL, NULL, 0, 0}
551   };
552
553   configuration = cfg;
554   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task, NULL);
555   GNUNET_SERVER_add_handlers (server, server_handlers);
556   stream_listen_socket = GNUNET_STREAM_listen (cfg, GNUNET_APPLICATION_TYPE_SET,
557                                                &stream_listen_cb, NULL,
558                                                GNUNET_STREAM_OPTION_END);
559 }
560
561
562 /**
563  * The main function for the set service.
564  *
565  * @param argc number of arguments from the command line
566  * @param argv command line arguments
567  * @return 0 ok, 1 on error
568  */
569 int
570 main (int argc, char *const *argv)
571 {
572   int ret;
573   ret = GNUNET_SERVICE_run (argc, argv, "set", GNUNET_SERVICE_OPTION_NONE, &run, NULL);
574   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "exit\n");
575   return (GNUNET_OK == ret) ? 0 : 1;
576 }
577
578