psyc, multicast: join decision
[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 MulticastJoinRequestMessage *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   if (NULL != mem->join_decision)
210     GNUNET_free (mem->join_decision);
211   GNUNET_CONTAINER_multihashmap_remove (members, &grp->pub_key_hash, mem);
212 }
213
214
215 /**
216  * Clean up group data structures after a client disconnected.
217  */
218 static void
219 cleanup_group (struct Group *grp)
220 {
221   (GNUNET_YES == grp->is_origin)
222     ? cleanup_origin ((struct Origin *) grp)
223     : cleanup_member ((struct Member *) grp);
224
225   GNUNET_free (grp);
226 }
227
228
229 /**
230  * Called whenever a client is disconnected.
231  *
232  * Frees our resources associated with that client.
233  *
234  * @param cls  Closure.
235  * @param client  Client handle.
236  */
237 static void
238 client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
239 {
240   if (NULL == client)
241     return;
242
243   struct Group *grp
244     = GNUNET_SERVER_client_get_user_context (client, struct Group);
245
246   if (NULL == grp)
247   {
248     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
249                 "%p User context is NULL in client_disconnect()\n", grp);
250     GNUNET_assert (0);
251     return;
252   }
253
254   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
255               "%p Client (%s) disconnected from group %s\n",
256               grp, (GNUNET_YES == grp->is_origin) ? "origin" : "member",
257               GNUNET_h2s (&grp->pub_key_hash));
258
259   struct ClientList *cl = grp->clients_head;
260   while (NULL != cl)
261   {
262     if (cl->client == client)
263     {
264       GNUNET_CONTAINER_DLL_remove (grp->clients_head, grp->clients_tail, cl);
265       GNUNET_free (cl);
266       break;
267     }
268     cl = cl->next;
269   }
270
271   if (NULL == grp->clients_head)
272   { /* Last client disconnected. */
273 #if FIXME
274     if (NULL != grp->tmit_head)
275     { /* Send pending messages via CADET before cleanup. */
276       transmit_message (grp);
277     }
278     else
279 #endif
280     {
281       cleanup_group (grp);
282     }
283   }
284 }
285
286
287 /**
288  * Send message to all clients connected to the group.
289  */
290 static void
291 message_to_clients (const struct Group *grp,
292                     const struct GNUNET_MessageHeader *msg)
293 {
294   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
295               "%p Sending message to clients.\n", grp);
296
297   struct ClientList *cl = grp->clients_head;
298   while (NULL != cl)
299   {
300     GNUNET_SERVER_notification_context_add (nc, cl->client);
301     GNUNET_SERVER_notification_context_unicast (nc, cl->client, msg, GNUNET_NO);
302     cl = cl->next;
303   }
304 }
305
306
307 /**
308  * Iterator callback for sending a message to origin clients.
309  */
310 static int
311 origin_message_cb (void *cls, const struct GNUNET_HashCode *pub_key_hash,
312                    void *origin)
313 {
314   const struct GNUNET_MessageHeader *msg = cls;
315   struct Member *orig = origin;
316
317   message_to_clients (&orig->grp, msg);
318   return GNUNET_YES;
319 }
320
321
322 /**
323  * Iterator callback for sending a message to member clients.
324  */
325 static int
326 member_message_cb (void *cls, const struct GNUNET_HashCode *pub_key_hash,
327                    void *member)
328 {
329   const struct GNUNET_MessageHeader *msg = cls;
330   struct Member *mem = member;
331
332   if (NULL != mem->join_decision)
333   { /* Only send message to admitted members */
334     message_to_clients (&mem->grp, msg);
335   }
336   return GNUNET_YES;
337 }
338
339
340 /**
341  * Send message to all origin and member clients connected to the group.
342  *
343  * @param grp  The group to send @a msg to.
344  * @param msg  Message to send.
345  */
346 static void
347 message_to_group (struct Group *grp, const struct GNUNET_MessageHeader *msg)
348 {
349   if (origins != NULL)
350     GNUNET_CONTAINER_multihashmap_get_multiple (origins, &grp->pub_key_hash,
351                                                 origin_message_cb, (void *) msg);
352   if (members != NULL)
353     GNUNET_CONTAINER_multihashmap_get_multiple (members, &grp->pub_key_hash,
354                                                 member_message_cb, (void *) msg);
355 }
356
357
358 /**
359  * Send message to all origin clients connected to the group.
360  *
361  * @param grp  The group to send @a msg to.
362  * @param msg  Message to send.
363  */
364 static void
365 message_to_origin (struct Group *grp, const struct GNUNET_MessageHeader *msg)
366 {
367   if (origins != NULL)
368     GNUNET_CONTAINER_multihashmap_get_multiple (origins, &grp->pub_key_hash,
369                                                 origin_message_cb, (void *) msg);
370 }
371
372
373 /**
374  * Handle a connecting client starting an origin.
375  */
376 static void
377 handle_origin_start (void *cls, struct GNUNET_SERVER_Client *client,
378                      const struct GNUNET_MessageHeader *m)
379 {
380   const struct MulticastOriginStartMessage *
381     msg = (const struct MulticastOriginStartMessage *) m;
382
383   struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
384   struct GNUNET_HashCode pub_key_hash;
385
386   GNUNET_CRYPTO_eddsa_key_get_public (&msg->group_key, &pub_key);
387   GNUNET_CRYPTO_hash (&pub_key, sizeof (pub_key), &pub_key_hash);
388
389   struct Origin *
390     orig = GNUNET_CONTAINER_multihashmap_get (origins, &pub_key_hash);
391   struct Group *grp;
392
393   if (NULL == orig)
394   {
395     orig = GNUNET_new (struct Origin);
396     orig->priv_key = msg->group_key;
397     grp = &orig->grp;
398     grp->is_origin = GNUNET_YES;
399     grp->pub_key = pub_key;
400     grp->pub_key_hash = pub_key_hash;
401
402     GNUNET_CONTAINER_multihashmap_put (origins, &grp->pub_key_hash, orig,
403                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
404   }
405   else
406   {
407     grp = &orig->grp;
408   }
409
410   struct ClientList *cl = GNUNET_new (struct ClientList);
411   cl->client = client;
412   GNUNET_CONTAINER_DLL_insert (grp->clients_head, grp->clients_tail, cl);
413
414   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
415               "%p Client connected as origin to group %s.\n",
416               orig, GNUNET_h2s (&grp->pub_key_hash));
417
418   GNUNET_SERVER_client_set_user_context (client, grp);
419   GNUNET_SERVER_receive_done (client, GNUNET_OK);
420 }
421
422
423 /**
424  * Handle a connecting client joining a group.
425  */
426 static void
427 handle_member_join (void *cls, struct GNUNET_SERVER_Client *client,
428                      const struct GNUNET_MessageHeader *m)
429 {
430   const struct MulticastMemberJoinMessage *
431     msg = (const struct MulticastMemberJoinMessage *) m;
432
433   struct GNUNET_CRYPTO_EddsaPublicKey mem_pub_key;
434   struct GNUNET_HashCode pub_key_hash, mem_pub_key_hash;
435
436   GNUNET_CRYPTO_eddsa_key_get_public (&msg->member_key, &mem_pub_key);
437   GNUNET_CRYPTO_hash (&mem_pub_key, sizeof (mem_pub_key), &mem_pub_key_hash);
438   GNUNET_CRYPTO_hash (&msg->group_key, sizeof (msg->group_key), &pub_key_hash);
439
440   struct GNUNET_CONTAINER_MultiHashMap *
441     grp_mem = GNUNET_CONTAINER_multihashmap_get (group_members, &pub_key_hash);
442   struct Member *mem = NULL;
443   struct Group *grp;
444
445   if (NULL != grp_mem)
446   {
447     mem = GNUNET_CONTAINER_multihashmap_get (grp_mem, &mem_pub_key_hash);
448   }
449   if (NULL == mem)
450   {
451     mem = GNUNET_new (struct Member);
452     mem->priv_key = msg->member_key;
453     mem->pub_key = mem_pub_key;
454     mem->pub_key_hash = mem_pub_key_hash;
455
456     grp = &mem->grp;
457     grp->is_origin = GNUNET_NO;
458     grp->pub_key = msg->group_key;
459     grp->pub_key_hash = pub_key_hash;
460
461     if (NULL == grp_mem)
462     {
463       grp_mem = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
464       GNUNET_CONTAINER_multihashmap_put (group_members, &grp->pub_key_hash, grp_mem,
465                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
466     }
467     GNUNET_CONTAINER_multihashmap_put (grp_mem, &mem->pub_key_hash, mem,
468                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
469     GNUNET_CONTAINER_multihashmap_put (members, &grp->pub_key_hash, mem,
470                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
471   }
472   else
473   {
474     grp = &mem->grp;
475   }
476
477   struct ClientList *cl = GNUNET_new (struct ClientList);
478   cl->client = client;
479   GNUNET_CONTAINER_DLL_insert (grp->clients_head, grp->clients_tail, cl);
480
481   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
482               "%p Client connected to group %s..\n",
483               mem, GNUNET_h2s (&grp->pub_key_hash));
484   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
485               "%p ..as member %s.\n",
486               mem, GNUNET_h2s (&mem_pub_key_hash));
487
488   GNUNET_SERVER_client_set_user_context (client, grp);
489
490   if (NULL != mem->join_decision)
491   { /* Already got a join decision, send it to client. */
492     GNUNET_SERVER_notification_context_add (nc, client);
493     GNUNET_SERVER_notification_context_unicast (nc, client,
494                                                 (struct GNUNET_MessageHeader *)
495                                                 mem->join_decision,
496                                                 GNUNET_NO);
497   }
498   else if (grp->clients_head == grp->clients_tail)
499   { /* First client, send join request. */
500     struct GNUNET_PeerIdentity *relays = (struct GNUNET_PeerIdentity *) &msg[1];
501     uint32_t relay_count = ntohs (msg->relay_count);
502     struct GNUNET_MessageHeader *
503       join_msg = ((struct GNUNET_MessageHeader *)
504                   ((char *) &msg[1]) + relay_count * sizeof (*relays));
505     uint16_t join_msg_size = ntohs (join_msg->size);
506
507     struct MulticastJoinRequestMessage *
508       req = GNUNET_malloc (sizeof (*req) + join_msg_size);
509     req->header.size = htons (sizeof (*req) + join_msg_size);
510     req->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_REQUEST);
511     req->group_key = grp->pub_key;
512     GNUNET_CRYPTO_eddsa_key_get_public (&mem->priv_key, &req->member_key);
513     memcpy (&req[1], join_msg, join_msg_size);
514
515     req->purpose.size = htonl (sizeof (*req) + join_msg_size
516                                - sizeof (req->header)
517                                - sizeof (req->signature));
518     req->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_REQUEST);
519
520     if (GNUNET_OK != GNUNET_CRYPTO_eddsa_sign (&mem->priv_key, &req->purpose,
521                                                &req->signature))
522     {
523       /* FIXME: handle error */
524       GNUNET_assert (0);
525     }
526
527     if (NULL != mem->join_request)
528       GNUNET_free (mem->join_request);
529     mem->join_request = req;
530
531     if (GNUNET_YES
532         == GNUNET_CONTAINER_multihashmap_contains (origins, &grp->pub_key_hash))
533     { /* Local origin */
534       message_to_origin (grp, (struct GNUNET_MessageHeader *) mem->join_request);
535     }
536     else
537     {
538       /* FIXME: send join request to remote peers */
539     }
540   }
541   GNUNET_SERVER_receive_done (client, GNUNET_OK);
542 }
543
544
545 /**
546  * Join decision from client.
547  */
548 static void
549 handle_join_decision (void *cls, struct GNUNET_SERVER_Client *client,
550                       const struct GNUNET_MessageHeader *m)
551 {
552   struct Group *
553     grp = GNUNET_SERVER_client_get_user_context (client, struct Group);
554   const struct MulticastClientJoinDecisionMessage *
555     cl_dcsn = (const struct MulticastClientJoinDecisionMessage *) m;
556
557   struct GNUNET_PeerIdentity *relays = (struct GNUNET_PeerIdentity *) &cl_dcsn[1];
558   uint32_t relay_count = ntohs (cl_dcsn->relay_count);
559
560   struct GNUNET_MessageHeader *join_msg = NULL;
561   uint16_t join_msg_size = 0;
562   if (sizeof (*cl_dcsn) + relay_count * sizeof (*relays) + sizeof (*m)
563       <= ntohs (m->size))
564   {
565     join_msg = ((struct GNUNET_MessageHeader *)
566                 ((char *) &cl_dcsn[1]) + relay_count * sizeof (*relays));
567     join_msg_size = ntohs (join_msg->size);
568   }
569
570   struct MulticastJoinDecisionMessage *
571     dcsn = GNUNET_malloc (sizeof (*dcsn) + join_msg_size);
572   dcsn->header.size = htons (sizeof (*dcsn) + join_msg_size);
573   dcsn->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_DECISION);
574   dcsn->is_admitted = cl_dcsn->is_admitted;
575   memcpy (&dcsn[1], join_msg, join_msg_size);
576
577   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
578               "%p Got join decision from client for group %s..\n",
579               grp, GNUNET_h2s (&grp->pub_key_hash));
580
581   if (GNUNET_YES
582       == GNUNET_CONTAINER_multihashmap_contains (origins, &grp->pub_key_hash))
583   { /* Local origin */
584     struct GNUNET_CONTAINER_MultiHashMap *
585       grp_mem = GNUNET_CONTAINER_multihashmap_get (group_members,
586                                                    &grp->pub_key_hash);
587     if (NULL != grp_mem)
588     {
589       struct GNUNET_HashCode member_key_hash;
590       GNUNET_CRYPTO_hash (&cl_dcsn->member_key, sizeof (cl_dcsn->member_key),
591                           &member_key_hash);
592       struct Member *
593         mem = GNUNET_CONTAINER_multihashmap_get (grp_mem, &member_key_hash);
594       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
595                   "%p ..and member %s: %p\n",
596                   grp, GNUNET_h2s (&member_key_hash), mem);
597       if (NULL != mem)
598       {
599         message_to_clients (grp, (struct GNUNET_MessageHeader *) dcsn);
600         if (GNUNET_YES == dcsn->is_admitted)
601         { /* Member admitted, store join_decision. */
602           mem->join_decision = dcsn;
603         }
604         else
605         { /* Refused entry, disconnect clients. */
606           GNUNET_free (dcsn);
607           struct ClientList *cl = mem->grp.clients_head;
608           while (NULL != cl)
609           {
610             GNUNET_SERVER_client_disconnect (cl->client);
611             cl = cl->next;
612           }
613         }
614       }
615     }
616   }
617   else
618   {
619     /* FIXME: send join decision to remote peers */
620   }
621   GNUNET_SERVER_receive_done (client, GNUNET_OK);
622 }
623
624 /**
625  * Incoming message from a client.
626  */
627 static void
628 handle_multicast_message (void *cls, struct GNUNET_SERVER_Client *client,
629                           const struct GNUNET_MessageHeader *m)
630 {
631   struct Group *
632     grp = GNUNET_SERVER_client_get_user_context (client, struct Group);
633   GNUNET_assert (GNUNET_YES == grp->is_origin);
634   struct Origin *orig = (struct Origin *) grp;
635   struct GNUNET_MULTICAST_MessageHeader *
636     msg = (struct GNUNET_MULTICAST_MessageHeader *) m;
637
638   msg->fragment_id = GNUNET_htonll (orig->max_fragment_id++);
639   msg->purpose.size = htonl (sizeof (*msg) + ntohs (m->size)
640                              - sizeof (msg->header)
641                              - sizeof (msg->hop_counter)
642                              - sizeof (msg->signature));
643   msg->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_MESSAGE);
644
645   if (GNUNET_OK != GNUNET_CRYPTO_eddsa_sign (&orig->priv_key, &msg->purpose,
646                                              &msg->signature))
647   {
648     /* FIXME: handle error */
649     GNUNET_assert (0);
650   }
651
652   /* FIXME: send to remote members */
653
654   message_to_group (grp, m);
655   GNUNET_SERVER_receive_done (client, GNUNET_OK);
656 }
657
658
659 /**
660  * Incoming request from a client.
661  */
662 static void
663 handle_multicast_request (void *cls, struct GNUNET_SERVER_Client *client,
664                           const struct GNUNET_MessageHeader *m)
665 {
666   struct Group *
667     grp = GNUNET_SERVER_client_get_user_context (client, struct Group);
668   GNUNET_assert (GNUNET_NO == grp->is_origin);
669   struct Member *mem = (struct Member *) grp;
670
671   struct GNUNET_MULTICAST_RequestHeader *
672     req = (struct GNUNET_MULTICAST_RequestHeader *) m;
673
674   req->fragment_id = GNUNET_ntohll (mem->max_fragment_id++);
675
676   req->purpose.size = htonl (sizeof (*req) + ntohs (m->size)
677                              - sizeof (req->header)
678                              - sizeof (req->member_key)
679                              - sizeof (req->signature));
680   req->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_MULTICAST_REQUEST);
681
682   if (GNUNET_OK != GNUNET_CRYPTO_eddsa_sign (&mem->priv_key, &req->purpose,
683                                              &req->signature))
684   {
685     /* FIXME: handle error */
686     GNUNET_assert (0);
687   }
688
689   if (GNUNET_YES
690       == GNUNET_CONTAINER_multihashmap_contains (origins, &grp->pub_key_hash))
691   { /* Local origin */
692     message_to_origin (grp, m);
693   }
694   else
695   {
696     /* FIXME: send to remote origin */
697   }
698   GNUNET_SERVER_receive_done (client, GNUNET_OK);
699 }
700
701 /**
702  * Process multicast requests.
703  *
704  * @param cls closure
705  * @param server the initialized server
706  * @param cfg configuration to use
707  */
708 static void
709 run (void *cls, struct GNUNET_SERVER_Handle *server,
710      const struct GNUNET_CONFIGURATION_Handle *c)
711 {
712   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
713     { &handle_origin_start, NULL,
714       GNUNET_MESSAGE_TYPE_MULTICAST_ORIGIN_START, 0 },
715
716     { &handle_member_join, NULL,
717       GNUNET_MESSAGE_TYPE_MULTICAST_MEMBER_JOIN, 0 },
718
719     { &handle_join_decision, NULL,
720       GNUNET_MESSAGE_TYPE_MULTICAST_JOIN_DECISION, 0 },
721
722     { &handle_multicast_message, NULL,
723       GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE, 0 },
724
725     { &handle_multicast_request, NULL,
726       GNUNET_MESSAGE_TYPE_MULTICAST_REQUEST, 0 },
727
728     {NULL, NULL, 0, 0}
729   };
730
731   cfg = c;
732   stats = GNUNET_STATISTICS_create ("multicast", cfg);
733   origins = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
734   members = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
735   group_members = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
736   nc = GNUNET_SERVER_notification_context_create (server, 1);
737
738   GNUNET_SERVER_add_handlers (server, handlers);
739   GNUNET_SERVER_disconnect_notify (server, &client_disconnect, NULL);
740   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task,
741                                 NULL);
742 }
743
744
745 /**
746  * The main function for the multicast service.
747  *
748  * @param argc number of arguments from the command line
749  * @param argv command line arguments
750  * @return 0 ok, 1 on error
751  */
752 int
753 main (int argc, char *const *argv)
754 {
755   return (GNUNET_OK ==
756           GNUNET_SERVICE_run (argc, argv, "multicast",
757                               GNUNET_SERVICE_OPTION_NONE, &run, NULL)) ? 0 : 1;
758 }
759
760 /* end of gnunet-service-multicast.c */