/*
* 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 "platform.h"
#include "gnunet_crypto_lib.h"
#include "gnunet_common.h"
#include "gnunet_util_lib.h"
#include "gnunet_testbed_service.h"
#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 */