2 This file is part of GNUnet.
3 (C) 2013 Christian Grothoff (and other contributing authors)
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.
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.
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.
21 * @file conversation/gnunet-service-conversation.c
22 * @brief conversation service implementation
23 * @author Simon Dieterle
24 * @author Andreas Fuchs
25 * @author Christian Grothoff
28 #include "gnunet_util_lib.h"
29 #include "gnunet_protocols.h"
30 #include "gnunet_applications.h"
31 #include "gnunet_constants.h"
32 #include "gnunet_signatures.h"
33 #include "gnunet_mesh_service.h"
34 #include "gnunet_conversation_service.h"
35 #include "conversation.h"
39 * How long is our signature on a call valid? Needs to be long enough for time zone
40 * differences and network latency to not matter. No strong need for it to be short,
41 * but we simply like all signatures to eventually expire.
43 #define RING_TIMEOUT GNUNET_TIME_UNIT_DAYS
47 * A line connects a local client with a mesh channel (or, if it is an
48 * open line, is waiting for a mesh channel).
53 * The possible connection status
58 * Our phone is ringing, waiting for the client to pick up.
68 * We're in shutdown, sending hangup messages before cleaning up.
73 * We are waiting for the phone to be picked up.
83 * We're in shutdown, sending hangup messages before cleaning up.
91 * A `struct Channel` represents a mesh channel, which is a P2P
92 * connection to another conversation service. Multiple channels can
93 * be attached the the same `struct Line`, which represents a local
94 * client. We keep them in a linked list.
102 struct Channel *next;
107 struct Channel *prev;
110 * Line associated with the channel.
115 * Handle for the reliable channel (contol data)
117 struct GNUNET_MESH_Channel *channel_reliable;
120 * Handle for unreliable channel (audio data)
122 struct GNUNET_MESH_Channel *channel_unreliable;
125 * Transmit handle for pending audio messages
127 struct GNUNET_MESH_TransmitHandle *unreliable_mth;
130 * Message queue for control messages
132 struct GNUNET_MQ_Handle *reliable_mq;
135 * Target of the line, if we are the caller.
137 struct GNUNET_PeerIdentity target;
140 * Temporary buffer for audio data.
145 * Number of bytes in @e audio_data.
150 * Channel identifier.
155 * Remote line number.
157 uint32_t remote_line;
160 * Current status of this line.
162 enum ChannelStatus status;
165 * #GNUNET_YES if the channel was suspended by the other peer.
167 int8_t suspended_remote;
170 * #GNUNET_YES if the channel was suspended by the local client.
172 int8_t suspended_local;
178 * A `struct Line` connects a local client with mesh channels.
195 struct Channel *channel_head;
200 struct Channel *channel_tail;
203 * Handle to the line client.
205 struct GNUNET_SERVER_Client *client;
208 * Generator for channel IDs.
223 static const struct GNUNET_CONFIGURATION_Handle *cfg;
226 * Notification context containing all connected clients.
228 static struct GNUNET_SERVER_NotificationContext *nc;
233 static struct GNUNET_MESH_Handle *mesh;
236 * Identity of this peer.
238 static struct GNUNET_PeerIdentity my_identity;
241 * Head of DLL of active lines.
243 static struct Line *lines_head;
246 * Tail of DLL of active lines.
248 static struct Line *lines_tail;
251 * Counter for generating local line numbers.
252 * FIXME: randomize generation in the future
253 * to eliminate information leakage.
255 static uint32_t local_line_cnt;
259 * Function to register a phone.
261 * @param cls closure, NULL
262 * @param client the client from which the message is
263 * @param message the message from the client
266 handle_client_register_message (void *cls,
267 struct GNUNET_SERVER_Client *client,
268 const struct GNUNET_MessageHeader *message)
270 const struct ClientPhoneRegisterMessage *msg;
273 msg = (const struct ClientPhoneRegisterMessage *) message;
274 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
278 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
281 line = GNUNET_new (struct Line);
282 line->client = client;
283 GNUNET_SERVER_notification_context_add (nc, client);
284 GNUNET_SERVER_client_set_user_context (client, line);
285 GNUNET_CONTAINER_DLL_insert (lines_head,
288 line->local_line = ntohl (msg->line) & (~ (1 << 31));
289 GNUNET_SERVER_receive_done (client, GNUNET_OK);
294 * Function to handle a pickup request message from the client
296 * @param cls closure, NULL
297 * @param client the client from which the message is
298 * @param message the message from the client
301 handle_client_pickup_message (void *cls,
302 struct GNUNET_SERVER_Client *client,
303 const struct GNUNET_MessageHeader *message)
305 const struct ClientPhonePickupMessage *msg;
306 struct GNUNET_MQ_Envelope *e;
307 struct MeshPhonePickupMessage *mppm;
311 msg = (const struct ClientPhonePickupMessage *) message;
312 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
316 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
319 for (ch = line->channel_head; NULL != ch; ch = ch->next)
320 if (msg->cid == ch->cid)
324 /* could have been destroyed asynchronously, ignore message */
325 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
326 "Channel %u not found\n",
332 case CS_CALLEE_RINGING:
333 ch->status = CS_CALLEE_CONNECTED;
335 case CS_CALLEE_CONNECTED:
337 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
339 case CS_CALLEE_SHUTDOWN:
340 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
341 "Ignoring client's PICKUP message, line is in SHUTDOWN\n");
342 GNUNET_SERVER_receive_done (client, GNUNET_OK);
344 case CS_CALLER_CALLING:
345 case CS_CALLER_CONNECTED:
346 case CS_CALLER_SHUTDOWN:
348 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
351 GNUNET_break (CS_CALLEE_CONNECTED == ch->status);
352 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
353 "Sending PICK_UP message to mesh\n");
354 e = GNUNET_MQ_msg (mppm,
355 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_PICK_UP);
356 GNUNET_MQ_send (ch->reliable_mq, e);
357 GNUNET_SERVER_receive_done (client, GNUNET_OK);
364 * @param ch channel to destroy.
367 destroy_line_mesh_channels (struct Channel *ch)
369 struct Line *line = ch->line;
370 struct GNUNET_MESH_Channel *t;
372 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
373 "Destroying mesh channels\n");
374 if (NULL != ch->reliable_mq)
376 GNUNET_MQ_destroy (ch->reliable_mq);
377 ch->reliable_mq = NULL;
379 if (NULL != ch->unreliable_mth)
381 GNUNET_MESH_notify_transmit_ready_cancel (ch->unreliable_mth);
382 ch->unreliable_mth = NULL;
384 if (NULL != (t = ch->channel_unreliable))
386 ch->channel_unreliable = NULL;
387 GNUNET_MESH_channel_destroy (t);
389 if (NULL != (t = ch->channel_reliable))
391 ch->channel_reliable = NULL;
392 GNUNET_MESH_channel_destroy (t);
394 GNUNET_CONTAINER_DLL_remove (line->channel_head,
397 GNUNET_free_non_null (ch->audio_data);
403 * We are done signalling shutdown to the other peer. Close down
406 * @param cls the `struct Channel` to reset/terminate
409 mq_done_finish_caller_shutdown (void *cls)
411 struct Channel *ch = cls;
415 case CS_CALLEE_RINGING:
418 case CS_CALLEE_CONNECTED:
421 case CS_CALLEE_SHUTDOWN:
422 destroy_line_mesh_channels (ch);
424 case CS_CALLER_CALLING:
427 case CS_CALLER_CONNECTED:
430 case CS_CALLER_SHUTDOWN:
431 destroy_line_mesh_channels (ch);
438 * Function to handle a hangup request message from the client
440 * @param cls closure, NULL
441 * @param client the client from which the message is
442 * @param message the message from the client
445 handle_client_hangup_message (void *cls,
446 struct GNUNET_SERVER_Client *client,
447 const struct GNUNET_MessageHeader *message)
449 const struct ClientPhoneHangupMessage *msg;
450 struct GNUNET_MQ_Envelope *e;
451 struct MeshPhoneHangupMessage *mhum;
455 msg = (const struct ClientPhoneHangupMessage *) message;
456 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
460 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
463 for (ch = line->channel_head; NULL != ch; ch = ch->next)
464 if (msg->cid == ch->cid)
468 /* could have been destroyed asynchronously, ignore message */
469 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
470 "Channel %u not found\n",
477 case CS_CALLEE_RINGING:
478 ch->status = CS_CALLEE_SHUTDOWN;
480 case CS_CALLEE_CONNECTED:
481 ch->status = CS_CALLEE_SHUTDOWN;
483 case CS_CALLEE_SHUTDOWN:
484 /* maybe the other peer closed asynchronously... */
486 case CS_CALLER_CALLING:
487 ch->status = CS_CALLER_SHUTDOWN;
489 case CS_CALLER_CONNECTED:
490 ch->status = CS_CALLER_SHUTDOWN;
492 case CS_CALLER_SHUTDOWN:
493 /* maybe the other peer closed asynchronously... */
496 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
497 "Sending HANG_UP message via mesh\n");
498 e = GNUNET_MQ_msg (mhum,
499 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP);
500 GNUNET_MQ_notify_sent (e,
501 &mq_done_finish_caller_shutdown,
503 GNUNET_MQ_send (ch->reliable_mq, e);
504 GNUNET_SERVER_receive_done (client, GNUNET_OK);
509 * Function to handle a suspend request message from the client
511 * @param cls closure, NULL
512 * @param client the client from which the message is
513 * @param message the message from the client
516 handle_client_suspend_message (void *cls,
517 struct GNUNET_SERVER_Client *client,
518 const struct GNUNET_MessageHeader *message)
520 const struct ClientPhoneSuspendMessage *msg;
521 struct GNUNET_MQ_Envelope *e;
522 struct MeshPhoneSuspendMessage *mhum;
526 msg = (const struct ClientPhoneSuspendMessage *) message;
527 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
531 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
534 for (ch = line->channel_head; NULL != ch; ch = ch->next)
535 if (msg->cid == ch->cid)
539 /* could have been destroyed asynchronously, ignore message */
540 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
541 "Channel %u not found\n",
545 if (GNUNET_YES == ch->suspended_local)
548 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
553 case CS_CALLEE_RINGING:
555 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
557 case CS_CALLEE_CONNECTED:
558 ch->suspended_local = GNUNET_YES;
560 case CS_CALLEE_SHUTDOWN:
561 /* maybe the other peer closed asynchronously... */
563 case CS_CALLER_CALLING:
565 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
567 case CS_CALLER_CONNECTED:
568 ch->suspended_local = GNUNET_YES;
570 case CS_CALLER_SHUTDOWN:
571 /* maybe the other peer closed asynchronously... */
574 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
575 "Sending SUSPEND message via mesh\n");
576 e = GNUNET_MQ_msg (mhum,
577 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_SUSPEND);
578 GNUNET_MQ_send (ch->reliable_mq, e);
579 GNUNET_SERVER_receive_done (client, GNUNET_OK);
584 * Function to handle a resume request message from the client
586 * @param cls closure, NULL
587 * @param client the client from which the message is
588 * @param message the message from the client
591 handle_client_resume_message (void *cls,
592 struct GNUNET_SERVER_Client *client,
593 const struct GNUNET_MessageHeader *message)
595 const struct ClientPhoneResumeMessage *msg;
596 struct GNUNET_MQ_Envelope *e;
597 struct MeshPhoneResumeMessage *mhum;
601 msg = (const struct ClientPhoneResumeMessage *) message;
602 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
606 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
609 for (ch = line->channel_head; NULL != ch; ch = ch->next)
610 if (msg->cid == ch->cid)
614 /* could have been destroyed asynchronously, ignore message */
615 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
616 "Channel %u not found\n",
620 if (GNUNET_YES != ch->suspended_local)
623 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
628 case CS_CALLEE_RINGING:
630 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
632 case CS_CALLEE_CONNECTED:
633 ch->suspended_local = GNUNET_NO;
635 case CS_CALLEE_SHUTDOWN:
636 /* maybe the other peer closed asynchronously... */
638 case CS_CALLER_CALLING:
640 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
642 case CS_CALLER_CONNECTED:
643 ch->suspended_local = GNUNET_NO;
645 case CS_CALLER_SHUTDOWN:
646 /* maybe the other peer closed asynchronously... */
649 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
650 "Sending RESUME message via mesh\n");
651 e = GNUNET_MQ_msg (mhum,
652 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RESUME);
653 GNUNET_MQ_send (ch->reliable_mq, e);
654 GNUNET_SERVER_receive_done (client, GNUNET_OK);
659 * Function to handle call request from the client
661 * @param cls closure, NULL
662 * @param client the client from which the message is
663 * @param message the message from the client
666 handle_client_call_message (void *cls,
667 struct GNUNET_SERVER_Client *client,
668 const struct GNUNET_MessageHeader *message)
670 const struct ClientCallMessage *msg;
673 struct GNUNET_MQ_Envelope *e;
674 struct MeshPhoneRingMessage *ring;
676 msg = (const struct ClientCallMessage *) message;
677 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
681 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
684 line = GNUNET_new (struct Line);
685 line->client = client;
686 line->local_line = (local_line_cnt++) | (1 << 31);
687 GNUNET_SERVER_client_set_user_context (client, line);
688 GNUNET_SERVER_notification_context_add (nc, client);
689 GNUNET_CONTAINER_DLL_insert (lines_head,
692 ch = GNUNET_new (struct Channel);
694 GNUNET_CONTAINER_DLL_insert (line->channel_head,
697 ch->target = msg->target;
698 ch->remote_line = ntohl (msg->line);
699 ch->status = CS_CALLER_CALLING;
700 ch->channel_reliable = GNUNET_MESH_channel_create (mesh,
703 GNUNET_APPLICATION_TYPE_CONVERSATION_CONTROL,
704 GNUNET_MESH_OPTION_RELIABLE);
705 ch->reliable_mq = GNUNET_MESH_mq_create (ch->channel_reliable);
706 e = GNUNET_MQ_msg (ring, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RING);
707 ring->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING);
708 ring->purpose.size = htonl (sizeof (struct GNUNET_PeerIdentity) * 2 +
709 sizeof (struct GNUNET_TIME_AbsoluteNBO) +
710 sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
711 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
712 GNUNET_CRYPTO_ecdsa_key_get_public (&msg->caller_id,
714 ring->remote_line = msg->line;
715 ring->source_line = line->local_line;
716 ring->target = msg->target;
717 ring->source = my_identity;
718 ring->expiration_time = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (RING_TIMEOUT));
719 GNUNET_CRYPTO_ecdsa_sign (&msg->caller_id,
722 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
723 "Sending RING message via mesh\n");
724 GNUNET_MQ_send (ch->reliable_mq, e);
725 GNUNET_SERVER_receive_done (client, GNUNET_OK);
730 * Transmit audio data via unreliable mesh channel.
732 * @param cls the `struct Channel` we are transmitting for
733 * @param size number of bytes available in @a buf
734 * @param buf where to copy the data
735 * @return number of bytes copied to @a buf
738 transmit_line_audio (void *cls,
742 struct Channel *ch = cls;
743 struct MeshAudioMessage *mam = buf;
745 ch->unreliable_mth = NULL;
746 if ( (NULL == buf) ||
747 (size < sizeof (struct MeshAudioMessage) + ch->audio_size) )
749 /* eh, other error handling? */
752 mam->header.size = htons (sizeof (struct MeshAudioMessage) + ch->audio_size);
753 mam->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_AUDIO);
754 mam->remote_line = htonl (ch->remote_line);
755 memcpy (&mam[1], ch->audio_data, ch->audio_size);
756 GNUNET_free (ch->audio_data);
757 ch->audio_data = NULL;
758 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
759 "Sending %u bytes of audio data on line %u via mesh\n",
760 ch->audio_size, ch->remote_line);
761 return sizeof (struct MeshAudioMessage) + ch->audio_size;
766 * Function to handle audio data from the client
768 * @param cls closure, NULL
769 * @param client the client from which the message is
770 * @param message the message from the client
773 handle_client_audio_message (void *cls,
774 struct GNUNET_SERVER_Client *client,
775 const struct GNUNET_MessageHeader *message)
777 const struct ClientAudioMessage *msg;
782 size = ntohs (message->size) - sizeof (struct ClientAudioMessage);
783 msg = (const struct ClientAudioMessage *) message;
784 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
788 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
791 for (ch = line->channel_head; NULL != ch; ch = ch->next)
792 if (msg->cid == ch->cid)
796 /* could have been destroyed asynchronously, ignore message */
797 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
798 "Channel %u not found\n",
805 case CS_CALLEE_RINGING:
806 case CS_CALLER_CALLING:
808 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
810 case CS_CALLEE_CONNECTED:
811 case CS_CALLER_CONNECTED:
812 /* common case, handled below */
814 case CS_CALLEE_SHUTDOWN:
815 case CS_CALLER_SHUTDOWN:
816 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
817 "Mesh audio channel in shutdown; audio data dropped\n");
818 GNUNET_SERVER_receive_done (client, GNUNET_OK);
821 if (NULL == ch->channel_unreliable)
823 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
824 _("Mesh audio channel not ready; audio data dropped\n"));
825 GNUNET_SERVER_receive_done (client, GNUNET_OK);
828 if (NULL != ch->unreliable_mth)
830 /* NOTE: we may want to not do this and instead combine the data */
831 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
832 "Bandwidth insufficient; dropping previous audio data segment with %u bytes\n",
833 (unsigned int) ch->audio_size);
834 GNUNET_MESH_notify_transmit_ready_cancel (ch->unreliable_mth);
835 ch->unreliable_mth = NULL;
836 GNUNET_free (ch->audio_data);
837 ch->audio_data = NULL;
839 ch->audio_size = size;
840 ch->audio_data = GNUNET_malloc (ch->audio_size);
841 memcpy (ch->audio_data,
844 ch->unreliable_mth = GNUNET_MESH_notify_transmit_ready (ch->channel_unreliable,
846 GNUNET_TIME_UNIT_FOREVER_REL,
847 sizeof (struct MeshAudioMessage)
849 &transmit_line_audio,
851 GNUNET_SERVER_receive_done (client, GNUNET_OK);
856 * We are done signalling shutdown to the other peer.
857 * Destroy the channel.
859 * @param cls the `struct GNUNET_MESH_channel` to destroy
862 mq_done_destroy_channel (void *cls)
864 struct GNUNET_MESH_Channel *channel = cls;
866 GNUNET_MESH_channel_destroy (channel);
871 * Function to handle a ring message incoming over mesh
873 * @param cls closure, NULL
874 * @param channel the channel over which the message arrived
875 * @param channel_ctx the channel context, can be NULL
876 * or point to the `struct Channel`
877 * @param message the incoming message
881 handle_mesh_ring_message (void *cls,
882 struct GNUNET_MESH_Channel *channel,
884 const struct GNUNET_MessageHeader *message)
886 const struct MeshPhoneRingMessage *msg;
889 struct GNUNET_MQ_Envelope *e;
890 struct MeshPhoneHangupMessage *hang_up;
891 struct ClientPhoneRingMessage cring;
892 struct GNUNET_MQ_Handle *reliable_mq;
894 msg = (const struct MeshPhoneRingMessage *) message;
895 if ( (msg->purpose.size != htonl (sizeof (struct GNUNET_PeerIdentity) * 2 +
896 sizeof (struct GNUNET_TIME_AbsoluteNBO) +
897 sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
898 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))) ||
900 GNUNET_CRYPTO_ecdsa_verify (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING,
906 return GNUNET_SYSERR;
908 for (line = lines_head; NULL != line; line = line->next)
909 if (line->local_line == ntohl (msg->remote_line))
913 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
914 _("No available phone for incoming call on line %u, sending HANG_UP signal\n"),
915 ntohl (msg->remote_line));
916 e = GNUNET_MQ_msg (hang_up,
917 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP);
918 GNUNET_MQ_notify_sent (e,
919 &mq_done_destroy_channel,
921 reliable_mq = GNUNET_MESH_mq_create (channel);
922 GNUNET_MQ_send (reliable_mq, e);
923 /* FIXME: do we need to clean up reliable_mq somehow/somewhere? */
924 GNUNET_MESH_receive_done (channel); /* needed? */
927 ch = GNUNET_new (struct Channel);
929 GNUNET_CONTAINER_DLL_insert (line->channel_head,
932 ch->status = CS_CALLEE_RINGING;
933 ch->remote_line = ntohl (msg->source_line);
934 ch->channel_reliable = channel;
935 ch->reliable_mq = GNUNET_MESH_mq_create (ch->channel_reliable);
936 ch->cid = line->cid_gen++;
937 ch->target = msg->source;
939 cring.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING);
940 cring.header.size = htons (sizeof (cring));
942 cring.caller_id = msg->caller_id;
943 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
944 "Sending RING message to client\n");
945 GNUNET_SERVER_notification_context_unicast (nc,
949 GNUNET_MESH_receive_done (channel);
955 * Function to handle a hangup message incoming over mesh
957 * @param cls closure, NULL
958 * @param channel the channel over which the message arrived
959 * @param channel_ctx the channel context, can be NULL
960 * or point to the `struct Channel`
961 * @param message the incoming message
965 handle_mesh_hangup_message (void *cls,
966 struct GNUNET_MESH_Channel *channel,
968 const struct GNUNET_MessageHeader *message)
970 struct Channel *ch = *channel_ctx;
972 struct ClientPhoneHangupMessage hup;
973 enum ChannelStatus status;
977 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
978 "HANGUP message received for non-existing line, dropping channel.\n");
979 return GNUNET_SYSERR;
983 hup.header.size = htons (sizeof (hup));
984 hup.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
987 GNUNET_MESH_receive_done (channel);
988 destroy_line_mesh_channels (ch);
991 case CS_CALLEE_RINGING:
992 case CS_CALLEE_CONNECTED:
994 case CS_CALLEE_SHUTDOWN:
996 case CS_CALLER_CALLING:
997 case CS_CALLER_CONNECTED:
999 case CS_CALLER_SHUTDOWN:
1002 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1003 "Sending HANG UP message to client\n");
1004 GNUNET_SERVER_notification_context_unicast (nc,
1013 * Function to handle a pickup message incoming over mesh
1015 * @param cls closure, NULL
1016 * @param channel the channel over which the message arrived
1017 * @param channel_ctx the channel context, can be NULL
1018 * or point to the `struct Channel`
1019 * @param message the incoming message
1020 * @return #GNUNET_OK
1023 handle_mesh_pickup_message (void *cls,
1024 struct GNUNET_MESH_Channel *channel,
1026 const struct GNUNET_MessageHeader *message)
1028 struct Channel *ch = *channel_ctx;
1030 struct ClientPhonePickupMessage pick;
1034 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1035 "PICKUP message received for non-existing channel, dropping channel.\n");
1036 return GNUNET_SYSERR;
1039 GNUNET_MESH_receive_done (channel);
1042 case CS_CALLEE_RINGING:
1043 case CS_CALLEE_CONNECTED:
1044 GNUNET_break_op (0);
1045 destroy_line_mesh_channels (ch);
1046 return GNUNET_SYSERR;
1047 case CS_CALLEE_SHUTDOWN:
1048 GNUNET_break_op (0);
1049 destroy_line_mesh_channels (ch);
1051 case CS_CALLER_CALLING:
1052 ch->status = CS_CALLER_CONNECTED;
1054 case CS_CALLER_CONNECTED:
1055 GNUNET_break_op (0);
1057 case CS_CALLER_SHUTDOWN:
1058 GNUNET_break_op (0);
1059 mq_done_finish_caller_shutdown (ch);
1060 return GNUNET_SYSERR;
1062 pick.header.size = htons (sizeof (pick));
1063 pick.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP);
1065 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1066 "Sending PICKED UP message to client\n");
1067 GNUNET_SERVER_notification_context_unicast (nc,
1071 ch->channel_unreliable = GNUNET_MESH_channel_create (mesh,
1074 GNUNET_APPLICATION_TYPE_CONVERSATION_AUDIO,
1075 GNUNET_MESH_OPTION_DEFAULT);
1076 if (NULL == ch->channel_unreliable)
1085 * Function to handle a suspend message incoming over mesh
1087 * @param cls closure, NULL
1088 * @param channel the channel over which the message arrived
1089 * @param channel_ctx the channel context, can be NULL
1090 * or point to the `struct Channel`
1091 * @param message the incoming message
1092 * @return #GNUNET_OK
1095 handle_mesh_suspend_message (void *cls,
1096 struct GNUNET_MESH_Channel *channel,
1098 const struct GNUNET_MessageHeader *message)
1100 struct Channel *ch = *channel_ctx;
1102 struct ClientPhoneSuspendMessage suspend;
1106 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1107 "SUSPEND message received for non-existing line, dropping channel.\n");
1108 return GNUNET_SYSERR;
1111 suspend.header.size = htons (sizeof (suspend));
1112 suspend.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND);
1113 suspend.cid = ch->cid;
1114 GNUNET_MESH_receive_done (channel);
1117 case CS_CALLEE_RINGING:
1118 GNUNET_break_op (0);
1120 case CS_CALLEE_CONNECTED:
1121 ch->suspended_remote = GNUNET_YES;
1123 case CS_CALLEE_SHUTDOWN:
1125 case CS_CALLER_CALLING:
1126 GNUNET_break_op (0);
1128 case CS_CALLER_CONNECTED:
1129 ch->suspended_remote = GNUNET_YES;
1131 case CS_CALLER_SHUTDOWN:
1134 GNUNET_SERVER_notification_context_unicast (nc,
1143 * Function to handle a resume message incoming over mesh
1145 * @param cls closure, NULL
1146 * @param channel the channel over which the message arrived
1147 * @param channel_ctx the channel context, can be NULL
1148 * or point to the `struct Channel`
1149 * @param message the incoming message
1150 * @return #GNUNET_OK
1153 handle_mesh_resume_message (void *cls,
1154 struct GNUNET_MESH_Channel *channel,
1156 const struct GNUNET_MessageHeader *message)
1158 struct Channel *ch = *channel_ctx;
1160 struct ClientPhoneResumeMessage resume;
1164 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1165 "RESUME message received for non-existing line, dropping channel.\n");
1166 return GNUNET_SYSERR;
1169 resume.header.size = htons (sizeof (resume));
1170 resume.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME);
1171 resume.cid = ch->cid;
1172 GNUNET_MESH_receive_done (channel);
1173 if (GNUNET_YES != ch->suspended_remote)
1175 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1176 "RESUME message received for non-suspended channel, dropping channel.\n");
1177 return GNUNET_SYSERR;
1181 case CS_CALLEE_RINGING:
1184 case CS_CALLEE_CONNECTED:
1185 ch->suspended_remote = GNUNET_NO;
1187 case CS_CALLEE_SHUTDOWN:
1189 case CS_CALLER_CALLING:
1192 case CS_CALLER_CONNECTED:
1193 ch->suspended_remote = GNUNET_NO;
1195 case CS_CALLER_SHUTDOWN:
1198 GNUNET_SERVER_notification_context_unicast (nc,
1207 * Function to handle an audio message incoming over mesh
1209 * @param cls closure, NULL
1210 * @param channel the channel over which the message arrived
1211 * @param channel_ctx the channel context, can be NULL
1212 * or point to the `struct Channel`
1213 * @param message the incoming message
1214 * @return #GNUNET_OK
1217 handle_mesh_audio_message (void *cls,
1218 struct GNUNET_MESH_Channel *channel,
1220 const struct GNUNET_MessageHeader *message)
1222 const struct MeshAudioMessage *msg;
1223 struct Channel *ch = *channel_ctx;
1225 struct GNUNET_PeerIdentity sender;
1226 size_t msize = ntohs (message->size) - sizeof (struct MeshAudioMessage);
1227 char buf[msize + sizeof (struct ClientAudioMessage)];
1228 struct ClientAudioMessage *cam;
1229 const union GNUNET_MESH_ChannelInfo *info;
1231 msg = (const struct MeshAudioMessage *) message;
1234 info = GNUNET_MESH_channel_get_info (channel,
1235 GNUNET_MESH_OPTION_PEER);
1241 sender = info->peer;
1242 for (line = lines_head; NULL != line; line = line->next)
1243 if (line->local_line == ntohl (msg->remote_line))
1245 for (ch = line->channel_head; NULL != ch; ch = ch->next)
1247 if ( (CS_CALLEE_CONNECTED == ch->status) &&
1248 (0 == memcmp (&ch->target,
1250 sizeof (struct GNUNET_PeerIdentity))) &&
1251 (NULL == ch->channel_unreliable) )
1257 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1258 "Received AUDIO data for non-existing line %u, dropping.\n",
1259 ntohl (msg->remote_line));
1260 return GNUNET_SYSERR;
1262 ch->channel_unreliable = channel;
1265 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1266 "Forwarding %u bytes of AUDIO data to client\n",
1268 cam = (struct ClientAudioMessage *) buf;
1269 cam->header.size = htons (sizeof (buf));
1270 cam->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO);
1272 memcpy (&cam[1], &msg[1], msize);
1273 GNUNET_SERVER_notification_context_unicast (nc,
1277 GNUNET_MESH_receive_done (channel);
1283 * Method called whenever another peer has added us to a channel
1284 * the other peer initiated.
1286 * @param cls closure
1287 * @param channel new handle to the channel
1288 * @param initiator peer that started the channel
1290 * @param options channel option flags
1291 * @return initial channel context for the channel;
1292 * (can be NULL -- that's not an error)
1295 inbound_channel (void *cls,
1296 struct GNUNET_MESH_Channel *channel,
1297 const struct GNUNET_PeerIdentity *initiator,
1298 uint32_t port, enum GNUNET_MESH_ChannelOption options)
1300 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1301 _("Received incoming channel on port %u\n"),
1302 (unsigned int) port);
1308 * Function called whenever an inbound channel is destroyed. Should clean up
1309 * any associated state.
1311 * @param cls closure (set from #GNUNET_MESH_connect)
1312 * @param channel connection to the other end (henceforth invalid)
1313 * @param channel_ctx place where local state associated
1314 * with the channel is stored;
1315 * may point to the `struct Channel`
1318 inbound_end (void *cls,
1319 const struct GNUNET_MESH_Channel *channel,
1322 struct Channel *ch = channel_ctx;
1324 struct ClientPhoneHangupMessage hup;
1328 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1329 "Mesh channel destroyed, but channel is unknown to us\n");
1333 if (ch->channel_unreliable == channel)
1335 if (NULL != ch->unreliable_mth)
1337 GNUNET_MESH_notify_transmit_ready_cancel (ch->unreliable_mth);
1338 ch->unreliable_mth = NULL;
1340 ch->channel_unreliable = NULL;
1341 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1342 "Unreliable channel destroyed\n");
1345 if (ch->channel_reliable != channel)
1347 /* recursive call, I'm the one destroying 'ch' right now */
1350 ch->channel_reliable = NULL;
1352 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1353 "Mesh channel destroyed by mesh in state %d\n",
1355 hup.header.size = htons (sizeof (hup));
1356 hup.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP);
1360 case CS_CALLEE_RINGING:
1361 case CS_CALLEE_CONNECTED:
1362 GNUNET_SERVER_notification_context_unicast (nc,
1367 case CS_CALLEE_SHUTDOWN:
1369 case CS_CALLER_CALLING:
1370 case CS_CALLER_CONNECTED:
1371 GNUNET_SERVER_notification_context_unicast (nc,
1376 case CS_CALLER_SHUTDOWN:
1379 destroy_line_mesh_channels (ch);
1384 * A client disconnected. Remove all of its data structure entries.
1386 * @param cls closure, NULL
1387 * @param client identification of the client
1390 handle_client_disconnect (void *cls,
1391 struct GNUNET_SERVER_Client *client)
1397 line = GNUNET_SERVER_client_get_user_context (client, struct Line);
1400 GNUNET_SERVER_client_set_user_context (client, (void *)NULL);
1401 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1402 "Client disconnected, closing line\n");
1403 GNUNET_CONTAINER_DLL_remove (lines_head,
1406 while (NULL != line->channel_head)
1407 destroy_line_mesh_channels (line->channel_head);
1415 * @param cls closure, NULL
1416 * @param tc the task context
1419 do_shutdown (void *cls,
1420 const struct GNUNET_SCHEDULER_TaskContext *tc)
1425 while (NULL != (line = lines_head))
1427 while (NULL != (ch = line->channel_head))
1428 destroy_line_mesh_channels (ch);
1429 GNUNET_CONTAINER_DLL_remove (lines_head,
1432 GNUNET_SERVER_client_set_user_context (line->client, (void *) NULL);
1437 GNUNET_MESH_disconnect (mesh);
1442 GNUNET_SERVER_notification_context_destroy (nc);
1449 * Main function that will be run by the scheduler.
1451 * @param cls closure
1452 * @param server server handle
1453 * @param c configuration
1457 struct GNUNET_SERVER_Handle *server,
1458 const struct GNUNET_CONFIGURATION_Handle *c)
1460 static const struct GNUNET_SERVER_MessageHandler server_handlers[] = {
1461 {&handle_client_register_message, NULL,
1462 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER,
1463 sizeof (struct ClientPhoneRegisterMessage)},
1464 {&handle_client_pickup_message, NULL,
1465 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP,
1466 sizeof (struct ClientPhonePickupMessage) },
1467 {&handle_client_suspend_message, NULL,
1468 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND,
1469 sizeof (struct ClientPhoneSuspendMessage) },
1470 {&handle_client_resume_message, NULL,
1471 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME,
1472 sizeof (struct ClientPhoneResumeMessage) },
1473 {&handle_client_hangup_message, NULL,
1474 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
1475 sizeof (struct ClientPhoneHangupMessage) },
1476 {&handle_client_call_message, NULL,
1477 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL,
1478 sizeof (struct ClientCallMessage) },
1479 {&handle_client_audio_message, NULL,
1480 GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
1484 static struct GNUNET_MESH_MessageHandler mesh_handlers[] = {
1485 {&handle_mesh_ring_message,
1486 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RING,
1487 sizeof (struct MeshPhoneRingMessage)},
1488 {&handle_mesh_hangup_message,
1489 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP,
1490 sizeof (struct MeshPhoneHangupMessage)},
1491 {&handle_mesh_pickup_message,
1492 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_PICK_UP,
1493 sizeof (struct MeshPhonePickupMessage)},
1494 {&handle_mesh_suspend_message,
1495 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_SUSPEND,
1496 sizeof (struct MeshPhoneSuspendMessage)},
1497 {&handle_mesh_resume_message,
1498 GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RESUME,
1499 sizeof (struct MeshPhoneResumeMessage)},
1500 {&handle_mesh_audio_message, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_AUDIO,
1504 static uint32_t ports[] = {
1505 GNUNET_APPLICATION_TYPE_CONVERSATION_CONTROL,
1506 GNUNET_APPLICATION_TYPE_CONVERSATION_AUDIO,
1511 GNUNET_assert (GNUNET_OK ==
1512 GNUNET_CRYPTO_get_peer_identity (cfg,
1514 mesh = GNUNET_MESH_connect (cfg,
1524 GNUNET_SCHEDULER_shutdown ();
1527 nc = GNUNET_SERVER_notification_context_create (server, 16);
1528 GNUNET_SERVER_add_handlers (server, server_handlers);
1529 GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL);
1530 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1537 * The main function for the conversation service.
1539 * @param argc number of arguments from the command line
1540 * @param argv command line arguments
1541 * @return 0 ok, 1 on error
1547 return (GNUNET_OK ==
1548 GNUNET_SERVICE_run (argc, argv,
1550 GNUNET_SERVICE_OPTION_NONE,
1551 &run, NULL)) ? 0 : 1;
1554 /* end of gnunet-service-conversation.c */