/* * This file is part of GNUnet * Copyright (C) 2013 GNUnet e.V. * * GNUnet is free software: you can redistribute it and/or modify it * under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * GNUnet is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . SPDX-License-Identifier: AGPL3.0-or-later */ /** * @file multicast/test_multicast_multipeers.c * @brief Tests for the Multicast API with multiple peers. * @author xrs */ #include #include #include #include #include #include #include "gnunet_multicast_service.h" #define PEERS_REQUESTED 12 struct MulticastPeerContext { int peer; /* peer number */ struct GNUNET_CRYPTO_EcdsaPrivateKey *key; const struct GNUNET_PeerIdentity *id; struct GNUNET_TESTBED_Operation *op; /* not yet in use */ struct GNUNET_TESTBED_Operation *pi_op; /* not yet in use */ int test_ok; }; enum pingpong { PING = 1, PONG = 2 }; struct pingpong_msg { int peer; enum pingpong msg; }; static void service_connect (void *cls, struct GNUNET_TESTBED_Operation *op, void *ca_result, const char *emsg); static struct MulticastPeerContext **multicast_peers; static struct GNUNET_TESTBED_Peer **peers; static struct GNUNET_TESTBED_Operation *op[PEERS_REQUESTED]; static struct GNUNET_TESTBED_Operation *pi_op[PEERS_REQUESTED]; static struct GNUNET_MULTICAST_Origin *origin; static struct GNUNET_MULTICAST_Member *members[PEERS_REQUESTED]; /* first element always empty */ static struct GNUNET_SCHEDULER_Task *timeout_tid; static struct GNUNET_CRYPTO_EddsaPrivateKey *group_key; static struct GNUNET_CRYPTO_EddsaPublicKey group_pub_key; static struct GNUNET_HashCode group_pub_key_hash; /** * Global result for testcase. */ static int result; /** * Function run on CTRL-C or shutdown (i.e. success/timeout/etc.). * Cleans up. */ static void shutdown_task (void *cls) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "shutdown_task!\n"); for (int i=0;ikey); GNUNET_free (multicast_peers[i]); multicast_peers[i] = NULL; } GNUNET_free (multicast_peers); multicast_peers = NULL; } if (NULL != timeout_tid) { GNUNET_SCHEDULER_cancel (timeout_tid); timeout_tid = NULL; } } static void timeout_task (void *cls) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Timeout!\n"); result = GNUNET_SYSERR; GNUNET_SCHEDULER_shutdown (); } static void member_join_request (void *cls, const struct GNUNET_CRYPTO_EcdsaPublicKey *member_pub_key, const struct GNUNET_MessageHeader *join_msg, struct GNUNET_MULTICAST_JoinHandle *jh) { struct MulticastPeerContext *mc_peer = (struct MulticastPeerContext*)cls; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Peer #%u (%s) sent a join request.\n", mc_peer->peer, GNUNET_i2s (multicast_peers[mc_peer->peer]->id)); } static int notify (void *cls, size_t *data_size, void *data) { struct MulticastPeerContext *mc_peer = (struct MulticastPeerContext*)cls; struct pingpong_msg *pp_msg = GNUNET_new (struct pingpong_msg); pp_msg->peer = mc_peer->peer; pp_msg->msg = PING; *data_size = sizeof (struct pingpong_msg); GNUNET_memcpy(data, pp_msg, *data_size); GNUNET_free (pp_msg); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Peer #%u sents ping to origin\n", mc_peer->peer); return GNUNET_YES; } static void member_join_decision (void *cls, int is_admitted, const struct GNUNET_PeerIdentity *peer, uint16_t relay_count, const struct GNUNET_PeerIdentity *relays, const struct GNUNET_MessageHeader *join_msg) { struct MulticastPeerContext *mc_peer = (struct MulticastPeerContext*)cls; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Peer #%u (%s) received a decision from origin: %s\n", mc_peer->peer, GNUNET_i2s (multicast_peers[mc_peer->peer]->id), (GNUNET_YES == is_admitted)?"accepted":"rejected"); if (GNUNET_YES == is_admitted) { GNUNET_MULTICAST_member_to_origin (members[mc_peer->peer], 0, notify, cls); } } static void member_replay_frag () { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "member replay frag...\n"); } static void member_replay_msg () { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "member replay msg...\n"); } static void origin_disconnected_cb (void *cls) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Origin disconnected. Shutting down.\n"); result = GNUNET_YES; GNUNET_SCHEDULER_shutdown (); } static void member_disconnected_cb (void *cls) { for (int i = 1; i < PEERS_REQUESTED; ++i) if (GNUNET_NO == multicast_peers[i]->test_ok) return; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "All member disconnected. Stopping origin.\n"); GNUNET_MULTICAST_origin_stop (origin, origin_disconnected_cb, cls); } static void member_message (void *cls, const struct GNUNET_MULTICAST_MessageHeader *msg) { struct MulticastPeerContext *mc_peer = (struct MulticastPeerContext*)cls; struct pingpong_msg *pp_msg = (struct pingpong_msg*) &(msg[1]); if (PONG == pp_msg->msg && mc_peer->peer == pp_msg->peer) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "peer #%i (%s) receives a pong\n", mc_peer->peer, GNUNET_i2s (multicast_peers[mc_peer->peer]->id)); mc_peer->test_ok = GNUNET_OK; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "peer #%u (%s) parting from multicast group\n", mc_peer->peer, GNUNET_i2s (multicast_peers[mc_peer->peer]->id)); GNUNET_MULTICAST_member_part (members[mc_peer->peer], member_disconnected_cb, cls); } } static void origin_join_request (void *cls, const struct GNUNET_CRYPTO_EcdsaPublicKey *member_pub_key, const struct GNUNET_MessageHeader *join_msg, struct GNUNET_MULTICAST_JoinHandle *jh) { struct GNUNET_MessageHeader *join_resp; uint8_t data_size = ntohs (join_msg->size); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin got a join request...\n"); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin receives: '%s'\n", (char *)&join_msg[1]); char data[] = "Come in!"; data_size = strlen (data) + 1; join_resp = GNUNET_malloc (sizeof (join_resp) + data_size); join_resp->size = htons (sizeof (join_resp) + data_size); join_resp->type = htons (123); GNUNET_memcpy (&join_resp[1], data, data_size); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin sends: '%s'\n", data); GNUNET_MULTICAST_join_decision (jh, GNUNET_YES, 0, NULL, join_resp); result = GNUNET_OK; } static void origin_replay_frag (void *cls, const struct GNUNET_CRYPTO_EcdsaPublicKey *member_pub_key, uint64_t fragment_id, uint64_t flags, struct GNUNET_MULTICAST_ReplayHandle *rh) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin replay fraq msg\n"); } static void origin_replay_msg (void *cls, const struct GNUNET_CRYPTO_EcdsaPublicKey *member_pub_key, uint64_t message_id, uint64_t fragment_offset, uint64_t flags, struct GNUNET_MULTICAST_ReplayHandle *rh) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin replay msg\n"); } static int origin_notify (void *cls, size_t *data_size, void *data) { struct pingpong_msg *rcv_pp_msg = (struct pingpong_msg*)cls; struct pingpong_msg *pp_msg = GNUNET_new (struct pingpong_msg); pp_msg->peer = rcv_pp_msg->peer; pp_msg->msg = PONG; *data_size = sizeof (struct pingpong_msg); GNUNET_memcpy(data, pp_msg, *data_size); GNUNET_free (pp_msg); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin sends pong\n"); return GNUNET_YES; } static void origin_request (void *cls, const struct GNUNET_MULTICAST_RequestHeader *req) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin receives a msg\n"); req++; struct pingpong_msg *pp_msg = (struct pingpong_msg *) req; if (1 != pp_msg->msg) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "origin didn't reveice a correct request"); } GNUNET_MULTICAST_origin_to_all (origin, 0, 0, origin_notify, pp_msg); } static void origin_message (void *cls, const struct GNUNET_MULTICAST_MessageHeader *msg) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin message msg\n"); } static void multicast_disconnect (void *cls, void *op_result) { } static void * multicast_connect (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg) { struct MulticastPeerContext *multicast_peer = cls; struct GNUNET_MessageHeader *join_msg; char data[64]; if (0 == multicast_peer->peer) { group_key = GNUNET_CRYPTO_eddsa_key_create (); GNUNET_CRYPTO_eddsa_key_get_public (group_key, &group_pub_key); GNUNET_CRYPTO_hash (&group_pub_key, sizeof (group_pub_key), &group_pub_key_hash); origin = GNUNET_MULTICAST_origin_start (cfg, group_key, 0, origin_join_request, origin_replay_frag, origin_replay_msg, origin_request, origin_message, cls); if (NULL == origin) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Peer #%u could not create a multicast group", multicast_peer->peer); return NULL; } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Peer #%u connected as origin to group %s\n", multicast_peer->peer, GNUNET_h2s (&group_pub_key_hash)); return origin; } else { multicast_peer->key = GNUNET_CRYPTO_ecdsa_key_create (); sprintf(data, "Hi, I am peer #%u (%s). Can I enter?", multicast_peer->peer, GNUNET_i2s (multicast_peers[multicast_peer->peer]->id)); uint8_t data_size = strlen (data) + 1; join_msg = GNUNET_malloc (sizeof (join_msg) + data_size); join_msg->size = htons (sizeof (join_msg) + data_size); join_msg->type = htons (123); GNUNET_memcpy (&join_msg[1], data, data_size); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Peer #%u (%s) tries to join multicast group %s\n", multicast_peer->peer, GNUNET_i2s (multicast_peers[multicast_peer->peer]->id), GNUNET_h2s (&group_pub_key_hash)); members[multicast_peer->peer] = GNUNET_MULTICAST_member_join (cfg, &group_pub_key, multicast_peer->key, multicast_peers[0]->id, 0, NULL, join_msg, /* join message */ member_join_request, member_join_decision, member_replay_frag, member_replay_msg, member_message, cls); return members[multicast_peer->peer]; } } static void peer_information_cb (void *cls, struct GNUNET_TESTBED_Operation *operation, const struct GNUNET_TESTBED_PeerInformation *pinfo, const char *emsg) { struct MulticastPeerContext *mc_peer = (struct MulticastPeerContext*)cls; if (NULL == pinfo) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "got no peer information\n"); result = GNUNET_SYSERR; GNUNET_SCHEDULER_shutdown (); } multicast_peers[mc_peer->peer]->id = pinfo->result.id; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got peer information of %s (%s)\n", (0 == mc_peer->peer)? "origin" : "member", GNUNET_i2s (pinfo->result.id)); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Create peer #%u (%s)\n", mc_peer->peer, GNUNET_i2s (multicast_peers[mc_peer->peer]->id)); if (0 != mc_peer->peer) { /* connect to multicast service of members */ op[mc_peer->peer] = GNUNET_TESTBED_service_connect (/* Closure for operation */ NULL, /* The peer whose service to connect to */ peers[mc_peer->peer], /* The name of the service */ "multicast", /* called after a handle to service is opened */ service_connect, /* closure for the above callback */ cls, /* called when opening the service connection */ multicast_connect, /* called when closing the service connection */ multicast_disconnect, /* closure for the above two callbacks */ cls); } } static void service_connect (void *cls, struct GNUNET_TESTBED_Operation *op, void *ca_result, const char *emsg) { struct MulticastPeerContext *mc_peer = (struct MulticastPeerContext*)cls; if (NULL == ca_result) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connection adapter not created for peer #%u (%s)\n", mc_peer->peer, GNUNET_i2s (multicast_peers[mc_peer->peer]->id)); result = GNUNET_SYSERR; GNUNET_SCHEDULER_shutdown(); } if (0 == mc_peer->peer) { // Get GNUnet identity of members for (int i = 0; ipeer = i; multicast_peers[i]->test_ok = GNUNET_NO; } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Create origin peer\n"); op[0] = GNUNET_TESTBED_service_connect (/* Closure for operation */ NULL, /* The peer whose service to connect to */ peers[0], /* The name of the service */ "multicast", /* called after a handle to service is opened */ service_connect, /* closure for the above callback */ multicast_peers[0], /* called when opening the service connection */ multicast_connect, /* called when closing the service connection */ multicast_disconnect, /* closure for the above two callbacks */ multicast_peers[0]); /* Schedule a new task on shutdown */ GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); /* Schedule the shutdown task with a delay of a few Seconds */ timeout_tid = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 400), &timeout_task, NULL); } int main (int argc, char *argv[]) { int ret; char const *config_file; if (strstr (argv[0], "_line") != NULL) { config_file = "test_multicast_line.conf"; } else if (strstr(argv[0], "_star") != NULL) { config_file = "test_multicast_star.conf"; } else { config_file = "test_multicast_star.conf"; } result = GNUNET_SYSERR; ret = GNUNET_TESTBED_test_run ("test-multicast-multipeer", config_file, /* number of peers to start */ PEERS_REQUESTED, /* Event mask - set to 0 for no event notifications */ 0LL, /* Controller event callback */ NULL, /* Closure for controller event callback */ NULL, /* called when testbed setup is complete */ testbed_master, /* Closure for the test_master callback */ NULL); if ( (GNUNET_OK != ret) || (GNUNET_OK != result) ) return 1; return 0; } /* end of test_multicast_multipeer.c */