multicast, psyc: client connections, join requests
[oweals/gnunet.git] / src / multicast / gnunet-service-multicast.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009 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 multicast/gnunet-service-multicast.c
23  * @brief program that does multicast
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_signatures.h"
29 #include "gnunet_statistics_service.h"
30 #include "gnunet_multicast_service.h"
31 #include "multicast.h"
32
33 /**
34  * Handle to our current configuration.
35  */
36 static const struct GNUNET_CONFIGURATION_Handle *cfg;
37
38 /**
39  * Handle to the statistics service.
40  */
41 static struct GNUNET_STATISTICS_Handle *stats;
42 /**
43  * Notification context, simplifies client broadcasts.
44  */
45 static struct GNUNET_SERVER_NotificationContext *nc;
46
47 /**
48  * All connected origins.
49  * Group's pub_key_hash -> struct Origin
50  */
51 static struct GNUNET_CONTAINER_MultiHashMap *origins;
52
53 /**
54  * All connected members.
55  * Group's pub_key_hash -> struct Member
56  */
57 static struct GNUNET_CONTAINER_MultiHashMap *members;
58
59 /**
60  * Connected members per group.
61  * Group's pub_key_hash -> Member's pub_key -> struct Member
62  */
63 static struct GNUNET_CONTAINER_MultiHashMap *group_members;
64
65
66 /**
67  * List of connected clients.
68  */
69 struct ClientList
70 {
71   struct ClientList *prev;
72   struct ClientList *next;
73   struct GNUNET_SERVER_Client *client;
74 };
75
76 /**
77  * Common part of the client context for both an origin and member.
78  */
79 struct Group
80 {
81   struct ClientList *clients_head;
82   struct ClientList *clients_tail;
83
84   /**
85    * Public key of the group.
86    */
87   struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
88
89   /**
90    * Hash of @a pub_key.
91    */
92   struct GNUNET_HashCode pub_key_hash;
93
94   /**
95    * Is this an origin (#GNUNET_YES), or member (#GNUNET_NO)?
96    */
97   uint8_t is_origin;
98
99   /**
100    * Is the client disconnected? #GNUNET_YES or #GNUNET_NO
101    */
102   uint8_t disconnected;
103 };
104
105
106 /**
107  * Client context for a group's origin.
108  */
109 struct Origin
110 {
111   struct Group grp;
112
113   /**
114    * Private key of the group.
115    */
116   struct GNUNET_CRYPTO_EddsaPrivateKey priv_key;
117
118   /**
119    * Last message fragment ID sent to the group.
120    */
121   uint64_t max_fragment_id;
122 };
123
124
125 /**
126  * Client context for a group member.
127  */
128 struct Member
129 {
130   struct Group grp;
131
132   /**
133    * Private key of the member.
134    */
135   struct GNUNET_CRYPTO_EddsaPrivateKey priv_key;
136
137   /**
138    * Public key of the member.
139    */
140   struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
141
142   /**
143    * Hash of @a pub_key.
144    */
145   struct GNUNET_HashCode pub_key_hash;
146
147   /**
148    * Join request sent to the origin / members.
149    */
150   struct GNUNET_MULTICAST_JoinRequest *join_request;
151
152   /**
153    * Join decision sent in reply to our request.
154    *
155    * Only a positive decision is stored here, in case of a negative decision the
156    * client is disconnected.
157    */
158   struct MulticastJoinDecisionMessage *join_decision;
159
160   /**
161    * Last request fragment ID sent to the origin.
162    */
163   uint64_t max_fragment_id;
164 };
165
166
167 /**
168  * Task run during shutdown.
169  *
170  * @param cls unused
171  * @param tc unused
172  */
173 static void
174 cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
175 {
176   /* FIXME: do clean up here */
177 }
178
179 /**
180  * Clean up origin data structures after a client disconnected.
181  */
182 static void
183 cleanup_origin (struct Origin *orig)
184 {
185   struct Group *grp = &orig->grp;
186   GNUNET_CONTAINER_multihashmap_remove (origins, &grp->pub_key_hash, orig);
187 }
188
189
190 /**
191  * Clean up member data structures after a client disconnected.
192  */
193 static void
194 cleanup_member (struct Member *mem)
195 {
196   struct Group *grp = &mem->grp;
197   struct GNUNET_CONTAINER_MultiHashMap *
198     grp_mem = GNUNET_CONTAINER_multihashmap_get (group_members,
199                                                  &grp->pub_key_hash);
200   GNUNET_assert (NULL != grp_mem);
201   GNUNET_CONTAINER_multihashmap_remove (grp_mem, &mem->pub_key_hash, mem);
202
203   if (0 == GNUNET_CONTAINER_multihashmap_size (grp_mem))
204   {
205     GNUNET_CONTAINER_multihashmap_remove (group_members, &grp->pub_key_hash,
206                                           grp_mem);
207     GNUNET_CONTAINER_multihashmap_destroy (grp_mem);
208   }
209   GNUNET_CONTAINER_multihashmap_remove (members, &grp->pub_key_hash, mem);
210 }
211
212
213 /**
214  * Clean up group data structures after a client disconnected.
215  */
216 static void
217 cleanup_group (struct Group *grp)
218 {
219   (GNUNET_YES == grp->is_origin)
220     ? cleanup_origin ((struct Origin *) grp)
221     : cleanup_member ((struct Member *) grp);
222
223   GNUNET_free (grp);
224 }
225
226
227 /**
228  * Called whenever a client is disconnected.
229  *
230  * Frees our resources associated with that client.
231  *
232  * @param cls  Closure.
233  * @param client  Client handle.
234  */
235 static void
236 client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
237 {
238   if (NULL == client)
239     return;
240
241   struct Group *grp
242     = GNUNET_SERVER_client_get_user_context (client, struct Group);
243
244   if (NULL == grp)
245   {
246     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
247                 "%p User context is NULL in client_disconnect()\n", grp);
248     GNUNET_assert (0);
249     return;
250   }
251
252   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
253               "%p Client (%s) disconnected from group %s\n",
254               grp, (GNUNET_YES == grp->is_origin) ? "origin" : "member",
255               GNUNET_h2s (&grp->pub_key_hash));
256
257   struct ClientList *cl = grp->clients_head;
258   while (NULL != cl)
259   {
260     if (cl->client == client)
261     {
262       GNUNET_CONTAINER_DLL_remove (grp->clients_head, grp->clients_tail, cl);
263       GNUNET_free (cl);
264       break;
265     }
266     cl = cl->next;
267   }
268
269   if (NULL == grp->clients_head)
270   { /* Last client disconnected. */
271 #if FIXME
272     if (NULL != grp->tmit_head)
273     { /* Send pending messages via CADET before cleanup. */
274       transmit_message (grp);
275     }
276     else
277 #endif
278     {
279       cleanup_group (grp);
280     }
281   }
282 }
283
284
285 /**
286  * Send message to all clients connected to the group.
287  */
288 static void
289 message_to_clients (const struct Group *grp,
290                     const struct GNUNET_MessageHeader *msg)
291 {
292   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
293               "%p Sending message to clients.\n", grp);
294
295   struct ClientList *cl = grp->clients_head;
296   while (NULL != cl)
297   {
298     GNUNET_SERVER_notification_context_add (nc, cl->client);
299     GNUNET_SERVER_notification_context_unicast (nc, cl->client, msg, GNUNET_NO);
300     cl = cl->next;
301   }
302 }
303
304
305 /**
306  * Iterator callback for sending a message to origin clients.
307  */
308 static int
309 origin_message_cb (void *cls, const struct GNUNET_HashCode *pub_key_hash,
310                    void *origin)
311 {
312   const struct GNUNET_MessageHeader *msg = cls;
313   struct Member *orig = origin;
314
315   message_to_clients (&orig->grp, msg);
316   return GNUNET_YES;
317 }
318
319
320 /**
321  * Iterator callback for sending a message to member clients.
322  */
323 static int
324 member_message_cb (void *cls, const struct GNUNET_HashCode *pub_key_hash,
325                    void *member)
326 {
327   const struct GNUNET_MessageHeader *msg = cls;
328   struct Member *mem = member;
329
330   if (NULL != mem->join_decision)
331   { /* Only send message to admitted members */
332     message_to_clients (&mem->grp, msg);
333   }
334   return GNUNET_YES;
335 }
336
337
338 /**
339  * Send message to all origin and member clients connected to the group.
340  *
341  * @param grp  The group to send @a msg to.
342  * @param msg  Message to send.
343  */
344 static void
345 message_to_group (struct Group *grp, const struct GNUNET_MessageHeader *msg)
346 {
347   if (origins != NULL)
348     GNUNET_CONTAINER_multihashmap_get_multiple (origins, &grp->pub_key_hash,
349                                                 origin_message_cb, (void *) msg);
350   if (members != NULL)
351     GNUNET_CONTAINER_multihashmap_get_multiple (members, &grp->pub_key_hash,
352                                                 member_message_cb, (void *) msg);
353 }
354
355
356 /**
357  * Send message to all origin clients connected to the group.
358  *
359  * @param grp  The group to send @a msg to.
360  * @param msg  Message to send.
361  */
362 static void
363 message_to_origin (struct Group *grp, const struct GNUNET_MessageHeader *msg)
364 {
365   if (origins != NULL)
366     GNUNET_CONTAINER_multihashmap_get_multiple (origins, &grp->pub_key_hash,
367                                                 origin_message_cb, (void *) msg);
368 }
369
370
371 /**
372  * Handle a connecting client starting an origin.
373  */
374 static void
375 handle_origin_start (void *cls, struct GNUNET_SERVER_Client *client,
376                      const struct GNUNET_MessageHeader *m)
377 {
378   const struct MulticastOriginStartMessage *
379     msg = (const struct MulticastOriginStartMessage *) m;
380
381   struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
382   struct GNUNET_HashCode pub_key_hash;
383
384   GNUNET_CRYPTO_eddsa_key_get_public (&msg->group_key, &pub_key);
385   GNUNET_CRYPTO_hash (&pub_key, sizeof (pub_key), &pub_key_hash);
386
387   struct Origin *
388     orig = GNUNET_CONTAINER_multihashmap_get (origins, &pub_key_hash);
389   struct Group *grp;
390
391   if (NULL == orig)
392   {
393     orig = GNUNET_new (struct Origin);
394     orig->priv_key = msg->group_key;
395     grp = &orig->grp;
396     grp->is_origin = GNUNET_YES;
397     grp->pub_key = pub_key;
398     grp->pub_key_hash = pub_key_hash;
399
400     GNUNET_CONTAINER_multihashmap_put (origins, &grp->pub_key_hash, orig,
401                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
402   }
403   else
404   {
405     grp = &orig->grp;
406   }
407
408   struct ClientList *cl = GNUNET_new (struct ClientList);
409   cl->client = client;
410   GNUNET_CONTAINER_DLL_insert (grp->clients_head, grp->clients_tail, cl);
411
412   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
413               "%p Client connected as origin to group %s.\n",
414               orig, GNUNET_h2s (&grp->pub_key_hash));
415
416   GNUNET_SERVER_client_set_user_context (client, grp);
417   GNUNET_SERVER_receive_done (client, GNUNET_OK);
418 }
419
420
421 /**
422  * Handle a connecting client joining a group.
423  */
424 static void
425 handle_member_join (void *cls, struct GNUNET_SERVER_Client *client,
426                      const struct GNUNET_MessageHeader *m)
427 {
428   struct MulticastMemberJoinMessage *
429     msg = (struct MulticastMemberJoinMessage *) m;
430
431   struct GNUNET_CRYPTO_EddsaPublicKey mem_pub_key;
432   struct GNUNET_HashCode pub_key_hash, mem_pub_key_hash;
433
434   GNUNET_CRYPTO_eddsa_key_get_public (&msg->member_key, &mem_pub_key);
435   GNUNET_CRYPTO_hash (&mem_pub_key, sizeof (mem_pub_key), &mem_pub_key_hash);
436   GNUNET_CRYPTO_hash (&msg->group_key, sizeof (msg->group_key), &pub_key_hash);
437
438   struct GNUNET_CONTAINER_MultiHashMap *
439     grp_mem = GNUNET_CONTAINER_multihashmap_get (group_members, &pub_key_hash);
440   struct Member *mem = NULL;
441   struct Group *grp;
442
443   if (NULL == grp_mem)
444   {
445     grp_mem = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
446     GNUNET_CONTAINER_multihashmap_put (group_members, &pub_key_hash, grp_mem,
447                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
448   }
449   else
450   {
451     mem = GNUNET_CONTAINER_multihashmap_get (grp_mem, &mem_pub_key_hash);
452   }
453
454   if (NULL == mem)
455   {
456     mem = GNUNET_new (struct Member);
457     mem->priv_key = msg->member_key;
458     mem->pub_key = mem_pub_key;
459     mem->pub_key_hash = mem_pub_key_hash;
460
461     grp = &mem->grp;
462     grp->is_origin = GNUNET_NO;
463     grp->pub_key = msg->group_key;
464     grp->pub_key_hash = pub_key_hash;
465
466     GNUNET_CONTAINER_multihashmap_put (grp_mem, &mem_pub_key_hash, mem,
467                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
468     GNUNET_CONTAINER_multihashmap_put (members, &grp->pub_key_hash, mem,
469                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
470   }
471   else
472   {
473     grp = &mem->grp;
474   }
475
476   struct ClientList *cl = GNUNET_new (struct ClientList);
477   cl->client = client;
478   GNUNET_CONTAINER_DLL_insert (grp->clients_head, grp->clients_tail, cl);
479
480   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
481               "%p Client connected as member to group %s.\n",
482               mem, GNUNET_h2s (&grp->pub_key_hash));
483
484   GNUNET_SERVER_client_set_user_context (client, grp);
485
486   if (NULL != mem->join_decision)
487   { /* Already got a join decision, send it to client. */
488     GNUNET_SERVER_notification_context_add (nc, client);
489     GNUNET_SERVER_notification_context_unicast (nc, client,
490                                                 (struct GNUNET_MessageHeader *)
491                                                 mem->join_decision,
492                                                 GNUNET_NO);
493   }
494   else if (grp->clients_head == grp->clients_tail)
495   { /* First client, send join request. */
496     struct GNUNET_PeerIdentity *relays = (struct GNUNET_PeerIdentity *) &msg[1];
497     uint32_t relay_count = ntohs (msg->relay_count);
498     struct GNUNET_MessageHeader *
499       join_req = ((struct GNUNET_MessageHeader *)
500                   ((char *) &msg[1]) + relay_count * sizeof (*relays));
501     uint16_t join_req_size = ntohs (join_req->size);
502
503     struct MulticastJoinRequestMessage *
504       req = GNUNET_malloc (sizeof (*req) + join_req_size);
505     req->header.size = htons (sizeof (*req) + join_req_size);
506     req->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_REQUEST);
507     req->group_key = grp->pub_key;
508     GNUNET_CRYPTO_eddsa_key_get_public (&mem->priv_key, &req->member_key);
509     memcpy (&req[1], join_req, join_req_size);
510
511     req->purpose.size = htonl (sizeof (*req) + join_req_size
512                                - sizeof (req->header)
513                                - sizeof (req->signature));
514     req->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_REQUEST);
515
516     if (GNUNET_OK != GNUNET_CRYPTO_eddsa_sign (&mem->priv_key, &req->purpose,
517                                                &req->signature))
518     {
519       /* FIXME: handle error */
520       GNUNET_assert (0);
521     }
522
523     if (NULL != mem->join_request)
524       GNUNET_free (mem->join_request);
525     mem->join_request = req;
526
527     if (GNUNET_YES
528         == GNUNET_CONTAINER_multihashmap_contains (origins, &grp->pub_key_hash))
529     { /* Local origin */
530       message_to_origin (grp, (struct GNUNET_MessageHeader *) mem->join_request);
531     }
532     else
533     {
534       /* FIXME: send join request to remote origin / members */
535     }
536   }
537   GNUNET_SERVER_receive_done (client, GNUNET_OK);
538 }
539
540
541 /**
542  * Incoming message from a client.
543  */
544 static void
545 handle_multicast_message (void *cls, struct GNUNET_SERVER_Client *client,
546                           const struct GNUNET_MessageHeader *m)
547 {
548   struct Group *
549     grp = GNUNET_SERVER_client_get_user_context (client, struct Group);
550   GNUNET_assert (GNUNET_YES == grp->is_origin);
551   struct Origin *orig = (struct Origin *) grp;
552   struct GNUNET_MULTICAST_MessageHeader *
553     msg = (struct GNUNET_MULTICAST_MessageHeader *) m;
554
555   msg->fragment_id = GNUNET_htonll (orig->max_fragment_id++);
556   msg->purpose.size = htonl (sizeof (*msg) + ntohs (m->size)
557                              - sizeof (msg->header)
558                              - sizeof (msg->hop_counter)
559                              - sizeof (msg->signature));
560   msg->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_MESSAGE);
561
562   if (GNUNET_OK != GNUNET_CRYPTO_eddsa_sign (&orig->priv_key, &msg->purpose,
563                                              &msg->signature))
564   {
565     /* FIXME: handle error */
566     GNUNET_assert (0);
567   }
568
569   /* FIXME: send to remote members */
570
571   message_to_group (grp, m);
572   GNUNET_SERVER_receive_done (client, GNUNET_OK);
573 }
574
575
576 /**
577  * Incoming request from a client.
578  */
579 static void
580 handle_multicast_request (void *cls, struct GNUNET_SERVER_Client *client,
581                           const struct GNUNET_MessageHeader *m)
582 {
583   struct Group *
584     grp = GNUNET_SERVER_client_get_user_context (client, struct Group);
585   GNUNET_assert (GNUNET_NO == grp->is_origin);
586   struct Member *mem = (struct Member *) grp;
587
588   struct GNUNET_MULTICAST_RequestHeader *
589     req = (struct GNUNET_MULTICAST_RequestHeader *) m;
590
591   req->fragment_id = GNUNET_ntohll (mem->max_fragment_id++);
592
593   req->purpose.size = htonl (sizeof (*req) + ntohs (m->size)
594                              - sizeof (req->header)
595                              - sizeof (req->member_key)
596                              - sizeof (req->signature));
597   req->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_REQUEST);
598
599   if (GNUNET_OK != GNUNET_CRYPTO_eddsa_sign (&mem->priv_key, &req->purpose,
600                                              &req->signature))
601   {
602     /* FIXME: handle error */
603     GNUNET_assert (0);
604   }
605
606   if (GNUNET_YES
607       == GNUNET_CONTAINER_multihashmap_contains (origins, &grp->pub_key_hash))
608   { /* Local origin */
609     message_to_origin (grp, m);
610   }
611   else
612   {
613     /* FIXME: send to remote origin */
614   }
615   GNUNET_SERVER_receive_done (client, GNUNET_OK);
616 }
617
618 /**
619  * Process multicast requests.
620  *
621  * @param cls closure
622  * @param server the initialized server
623  * @param cfg configuration to use
624  */
625 static void
626 run (void *cls, struct GNUNET_SERVER_Handle *server,
627      const struct GNUNET_CONFIGURATION_Handle *c)
628 {
629   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
630     { &handle_origin_start, NULL,
631       GNUNET_MESSAGE_TYPE_MULTICAST_ORIGIN_START, 0 },
632
633     { &handle_member_join, NULL,
634       GNUNET_MESSAGE_TYPE_MULTICAST_MEMBER_JOIN, 0 },
635
636     { &handle_multicast_message, NULL,
637       GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE, 0 },
638
639     { &handle_multicast_request, NULL,
640       GNUNET_MESSAGE_TYPE_MULTICAST_REQUEST, 0 },
641
642     {NULL, NULL, 0, 0}
643   };
644
645   cfg = c;
646   stats = GNUNET_STATISTICS_create ("multicast", cfg);
647   origins = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
648   members = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
649   group_members = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
650   nc = GNUNET_SERVER_notification_context_create (server, 1);
651
652   GNUNET_SERVER_add_handlers (server, handlers);
653   GNUNET_SERVER_disconnect_notify (server, &client_disconnect, NULL);
654   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task,
655                                 NULL);
656 }
657
658
659 /**
660  * The main function for the multicast service.
661  *
662  * @param argc number of arguments from the command line
663  * @param argv command line arguments
664  * @return 0 ok, 1 on error
665  */
666 int
667 main (int argc, char *const *argv)
668 {
669   return (GNUNET_OK ==
670           GNUNET_SERVICE_run (argc, argv, "multicast",
671                               GNUNET_SERVICE_OPTION_NONE, &run, NULL)) ? 0 : 1;
672 }
673
674 /* end of gnunet-service-multicast.c */