/* This file is part of GNUnet. Copyright (C) 2012, 2017 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 cadet/cadet_test_lib.c * @author Bartlomiej Polot * @brief library for writing CADET tests */ #include "platform.h" #include "gnunet_util_lib.h" #include "cadet_test_lib.h" #include "gnunet_cadet_service.h" /** * Test context for a CADET Test. */ struct GNUNET_CADET_TEST_Context { /** * Array of running peers. */ struct GNUNET_TESTBED_Peer **peers; /** * Array of handles to the CADET for each peer. */ struct GNUNET_CADET_Handle **cadets; /** * Operation associated with the connection to the CADET. */ struct GNUNET_TESTBED_Operation **ops; /** * Number of peers running, size of the arrays above. */ unsigned int num_peers; /** * Main function of the test to run once all CADETs are available. */ GNUNET_CADET_TEST_AppMain app_main; /** * Closure for 'app_main'. */ void *app_main_cls; /** * Handler for incoming tunnels. */ GNUNET_CADET_ConnectEventHandler connects; /** * Function called when the transmit window size changes. */ GNUNET_CADET_WindowSizeEventHandler window_changes; /** * Cleaner for destroyed incoming tunnels. */ GNUNET_CADET_DisconnectEventHandler disconnects; /** * Message handlers. */ struct GNUNET_MQ_MessageHandler *handlers; /** * Application ports. */ const struct GNUNET_HashCode **ports; /** * Number of ports in #ports. */ unsigned int port_count; }; /** * Context for a cadet adapter callback. */ struct GNUNET_CADET_TEST_AdapterContext { /** * Peer number for the particular peer. */ unsigned int peer; /** * Port handlers for open ports. */ struct GNUNET_CADET_Port **ports; /** * General context. */ struct GNUNET_CADET_TEST_Context *ctx; }; /** * Adapter function called to establish a connection to * the CADET service. * * @param cls closure * @param cfg configuration of the peer to connect to; will be available until * GNUNET_TESTBED_operation_done() is called on the operation returned * from GNUNET_TESTBED_service_connect() * @return service handle to return in 'op_result', NULL on error */ static void * cadet_connect_adapter (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg) { struct GNUNET_CADET_TEST_AdapterContext *actx = cls; struct GNUNET_CADET_TEST_Context *ctx = actx->ctx; struct GNUNET_CADET_Handle *h; h = GNUNET_CADET_connect (cfg); if (NULL == h) { GNUNET_break(0); return NULL; } if (NULL == ctx->ports) return h; actx->ports = GNUNET_new_array (ctx->port_count, struct GNUNET_CADET_Port *); for (unsigned int i = 0; i < ctx->port_count; i++) { actx->ports[i] = GNUNET_CADET_open_port (h, ctx->ports[i], ctx->connects, (void *) (long) actx->peer, ctx->window_changes, ctx->disconnects, ctx->handlers); } return h; } /** * Adapter function called to destroy a connection to * the CADET service. * * @param cls closure * @param op_result service handle returned from the connect adapter */ static void cadet_disconnect_adapter (void *cls, void *op_result) { struct GNUNET_CADET_Handle *cadet = op_result; struct GNUNET_CADET_TEST_AdapterContext *actx = cls; if (NULL != actx->ports) { for (unsigned int i = 0; i < actx->ctx->port_count; i++) { GNUNET_CADET_close_port (actx->ports[i]); actx->ports[i] = NULL; } GNUNET_free (actx->ports); } GNUNET_free (actx); GNUNET_CADET_disconnect (cadet); } /** * Callback to be called when a service connect operation is completed. * * @param cls The callback closure from functions generating an operation. * @param op The operation that has been finished. * @param ca_result The service handle returned from * GNUNET_TESTBED_ConnectAdapter() (cadet handle). * @param emsg Error message in case the operation has failed. * NULL if operation has executed successfully. */ static void cadet_connect_cb (void *cls, struct GNUNET_TESTBED_Operation *op, void *ca_result, const char *emsg) { struct GNUNET_CADET_TEST_Context *ctx = cls; if (NULL != emsg) { fprintf (stderr, "Failed to connect to CADET service: %s\n", emsg); GNUNET_SCHEDULER_shutdown (); return; } for (unsigned int i = 0; i < ctx->num_peers; i++) if (op == ctx->ops[i]) { ctx->cadets[i] = ca_result; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "...cadet %u connected\n", i); } for (unsigned int i = 0; i < ctx->num_peers; i++) if (NULL == ctx->cadets[i]) return; /* still some CADET connections missing */ /* all CADET connections ready! */ ctx->app_main (ctx->app_main_cls, ctx, ctx->num_peers, ctx->peers, ctx->cadets); } void GNUNET_CADET_TEST_cleanup (struct GNUNET_CADET_TEST_Context *ctx) { for (unsigned int i = 0; i < ctx->num_peers; i++) { GNUNET_assert (NULL != ctx->ops[i]); GNUNET_TESTBED_operation_done (ctx->ops[i]); ctx->ops[i] = NULL; } GNUNET_free (ctx->ops); GNUNET_free (ctx->cadets); GNUNET_free (ctx->handlers); GNUNET_free (ctx); GNUNET_SCHEDULER_shutdown (); } /** * Callback run when the testbed is ready (peers running and connected to * each other) * * @param cls Closure (context). * @param h the run handle * @param num_peers Number of peers that are running. * @param peers Handles to each one of the @c num_peers peers. * @param links_succeeded the number of overlay link connection attempts that * succeeded * @param links_failed the number of overlay link connection attempts that * failed */ static void cadet_test_run (void *cls, struct GNUNET_TESTBED_RunHandle *h, unsigned int num_peers, struct GNUNET_TESTBED_Peer **peers, unsigned int links_succeeded, unsigned int links_failed) { struct GNUNET_CADET_TEST_Context *ctx = cls; if (0 != links_failed) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Some links failed (%u), ending\n", links_failed); exit (77); } if (num_peers != ctx->num_peers) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Peers started %u/%u, ending\n", num_peers, ctx->num_peers); exit (1); } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Testbed up, %u peers and %u links\n", num_peers, links_succeeded); ctx->peers = peers; for (unsigned int i = 0; i < num_peers; i++) { struct GNUNET_CADET_TEST_AdapterContext *newctx; newctx = GNUNET_new (struct GNUNET_CADET_TEST_AdapterContext); newctx->peer = i; newctx->ctx = ctx; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Connecting to cadet %u\n", i); ctx->ops[i] = GNUNET_TESTBED_service_connect (ctx, peers[i], "cadet", &cadet_connect_cb, ctx, &cadet_connect_adapter, &cadet_disconnect_adapter, newctx); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "op handle %p\n", ctx->ops[i]); } } /** * Run a test using the given name, configuration file and number of peers. * All cadet callbacks will receive the peer number (long) as the closure. * * @param testname Name of the test (for logging). * @param cfgfile Name of the configuration file. * @param num_peers Number of peers to start. * @param tmain Main function to run once the testbed is ready. * @param tmain_cls Closure for @a tmain. * @param connects Handler for incoming channels. * @param window_changes Handler for the window size change notification. * @param disconnects Cleaner for destroyed incoming channels. * @param handlers Message handlers. * @param ports Ports the peers offer, NULL-terminated. */ void GNUNET_CADET_TEST_ruN (const char *testname, const char *cfgfile, unsigned int num_peers, GNUNET_CADET_TEST_AppMain tmain, void *tmain_cls, GNUNET_CADET_ConnectEventHandler connects, GNUNET_CADET_WindowSizeEventHandler window_changes, GNUNET_CADET_DisconnectEventHandler disconnects, struct GNUNET_MQ_MessageHandler *handlers, const struct GNUNET_HashCode **ports) { struct GNUNET_CADET_TEST_Context *ctx; ctx = GNUNET_new (struct GNUNET_CADET_TEST_Context); ctx->num_peers = num_peers; ctx->ops = GNUNET_new_array (num_peers, struct GNUNET_TESTBED_Operation *); ctx->cadets = GNUNET_new_array (num_peers, struct GNUNET_CADET_Handle *); ctx->app_main = tmain; ctx->app_main_cls = tmain_cls; ctx->connects = connects; ctx->window_changes = window_changes; ctx->disconnects = disconnects; ctx->handlers = GNUNET_MQ_copy_handlers (handlers); ctx->ports = ports; ctx->port_count = 0; while (NULL != ctx->ports[ctx->port_count]) ctx->port_count++; GNUNET_TESTBED_test_run (testname, cfgfile, num_peers, 0LL, NULL, NULL, &cadet_test_run, ctx); } /* end of cadet_test_lib.c */