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