/* * 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 */ /** * @author Gabor X Toth * @author Christian Grothoff * * @file * Test for the PSYCstore plugins. */ #include #include #include #include #include "gnunet_psycstore_plugin.h" #include "gnunet_psycstore_service.h" #include "gnunet_multicast_service.h" #define DEBUG_PSYCSTORE GNUNET_EXTRA_LOGGING #if DEBUG_PSYCSTORE # define LOG_LEVEL "DEBUG" #else # define LOG_LEVEL "WARNING" #endif #define C2ARG(str) str, (sizeof (str) - 1) #define LOG(kind,...) \ GNUNET_log_from (kind, "test-plugin-psycstore", __VA_ARGS__) static int ok; /** * Name of plugin under test. */ static const char *plugin_name; static struct GNUNET_CRYPTO_EddsaPrivateKey *channel_key; static struct GNUNET_CRYPTO_EcdsaPrivateKey *slave_key; static struct GNUNET_CRYPTO_EddsaPublicKey channel_pub_key; static struct GNUNET_CRYPTO_EcdsaPublicKey slave_pub_key; /** * Function called when the service shuts down. Unloads our psycstore * plugin. * * @param api api to unload */ static void unload_plugin (struct GNUNET_PSYCSTORE_PluginFunctions *api) { char *libname; GNUNET_asprintf (&libname, "libgnunet_plugin_psycstore_%s", plugin_name); GNUNET_break (NULL == GNUNET_PLUGIN_unload (libname, api)); GNUNET_free (libname); } /** * Load the psycstore plugin. * * @param cfg configuration to pass * @return NULL on error */ static struct GNUNET_PSYCSTORE_PluginFunctions * load_plugin (const struct GNUNET_CONFIGURATION_Handle *cfg) { struct GNUNET_PSYCSTORE_PluginFunctions *ret; char *libname; GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Loading `%s' psycstore plugin\n"), plugin_name); GNUNET_asprintf (&libname, "libgnunet_plugin_psycstore_%s", plugin_name); if (NULL == (ret = GNUNET_PLUGIN_load (libname, (void*) cfg))) { fprintf (stderr, "Failed to load plugin `%s'!\n", plugin_name); return NULL; } GNUNET_free (libname); return ret; } #define MAX_MSG 16 struct FragmentClosure { uint8_t n; uint64_t flags[MAX_MSG]; struct GNUNET_MULTICAST_MessageHeader *msg[MAX_MSG]; }; static int fragment_cb (void *cls, struct GNUNET_MULTICAST_MessageHeader *msg2, enum GNUNET_PSYCSTORE_MessageFlags flags) { struct FragmentClosure *fcls = cls; struct GNUNET_MULTICAST_MessageHeader *msg1; uint64_t flags1; int ret; if (fcls->n >= MAX_MSG) { GNUNET_break (0); return GNUNET_SYSERR; } msg1 = fcls->msg[fcls->n]; flags1 = fcls->flags[fcls->n++]; if (NULL == msg1) { GNUNET_break (0); return GNUNET_SYSERR; } if (flags1 == flags && msg1->header.size == msg2->header.size && 0 == memcmp (msg1, msg2, ntohs (msg1->header.size))) { LOG (GNUNET_ERROR_TYPE_DEBUG, "Fragment %llu matches\n", GNUNET_ntohll (msg1->fragment_id)); ret = GNUNET_YES; } else { LOG (GNUNET_ERROR_TYPE_ERROR, "Fragment %llu differs\n", GNUNET_ntohll (msg1->fragment_id)); ret = GNUNET_SYSERR; } GNUNET_free (msg2); return ret; } struct StateClosure { size_t n; char *name[16]; void *value[16]; size_t value_size[16]; }; static int state_cb (void *cls, const char *name, const void *value, uint32_t value_size) { struct StateClosure *scls = cls; const void *val = scls->value[scls->n]; // FIXME: check for n out-of-bounds FIRST! size_t val_size = scls->value_size[scls->n++]; /* FIXME: check name */ LOG (GNUNET_ERROR_TYPE_DEBUG, " name = %s, value_size = %u\n", name, value_size); return GNUNET_YES; return value_size == val_size && 0 == memcmp (value, val, val_size) ? GNUNET_YES : GNUNET_SYSERR; } static void run (void *cls, char *const *args, const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg) { struct GNUNET_PSYCSTORE_PluginFunctions *db; ok = 1; db = load_plugin (cfg); if (NULL == db) { fprintf (stderr, "%s", "Failed to initialize PSYCstore. " "Database likely not setup, skipping test.\n"); ok = 77; return; } /* Store & test membership */ LOG (GNUNET_ERROR_TYPE_INFO, "MEMBERSHIP\n"); channel_key = GNUNET_CRYPTO_eddsa_key_create (); slave_key = GNUNET_CRYPTO_ecdsa_key_create (); GNUNET_CRYPTO_eddsa_key_get_public (channel_key, &channel_pub_key); GNUNET_CRYPTO_ecdsa_key_get_public (slave_key, &slave_pub_key); LOG (GNUNET_ERROR_TYPE_INFO, "membership_store()\n"); GNUNET_assert (GNUNET_OK == db->membership_store (db->cls, &channel_pub_key, &slave_pub_key, GNUNET_YES, 4, 2, 1)); LOG (GNUNET_ERROR_TYPE_INFO, "membership_test()\n"); GNUNET_assert (GNUNET_YES == db->membership_test (db->cls, &channel_pub_key, &slave_pub_key, 4)); GNUNET_assert (GNUNET_YES == db->membership_test (db->cls, &channel_pub_key, &slave_pub_key, 2)); GNUNET_assert (GNUNET_NO == db->membership_test (db->cls, &channel_pub_key, &slave_pub_key, 1)); /* Store & get messages */ LOG (GNUNET_ERROR_TYPE_INFO, "MESSAGES\n"); struct GNUNET_MULTICAST_MessageHeader *msg = GNUNET_malloc (sizeof (*msg) + sizeof (channel_pub_key)); GNUNET_assert (msg != NULL); msg->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE); msg->header.size = htons (sizeof (*msg) + sizeof (channel_pub_key)); uint64_t fragment_id = INT64_MAX - 1; msg->fragment_id = GNUNET_htonll (fragment_id); uint64_t message_id = INT64_MAX - 10; msg->message_id = GNUNET_htonll (message_id); uint64_t group_generation = INT64_MAX - 3; msg->group_generation = GNUNET_htonll (group_generation); msg->hop_counter = htonl (9); msg->fragment_offset = GNUNET_htonll (0); msg->flags = htonl (GNUNET_MULTICAST_MESSAGE_LAST_FRAGMENT); GNUNET_memcpy (&msg[1], &channel_pub_key, sizeof (channel_pub_key)); msg->purpose.size = htonl (ntohs (msg->header.size) - sizeof (msg->header) - sizeof (msg->hop_counter) - sizeof (msg->signature)); msg->purpose.purpose = htonl (234); GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_eddsa_sign (channel_key, &msg->purpose, &msg->signature)); LOG (GNUNET_ERROR_TYPE_INFO, "fragment_store()\n"); struct FragmentClosure fcls = { 0 }; fcls.n = 0; fcls.msg[0] = msg; fcls.flags[0] = GNUNET_PSYCSTORE_MESSAGE_STATE; GNUNET_assert ( GNUNET_OK == db->fragment_store (db->cls, &channel_pub_key, msg, fcls.flags[0])); LOG (GNUNET_ERROR_TYPE_INFO, "fragment_get(%" PRIu64 ")\n", fragment_id); uint64_t ret_frags = 0; GNUNET_assert ( GNUNET_OK == db->fragment_get (db->cls, &channel_pub_key, fragment_id, fragment_id, &ret_frags, fragment_cb, &fcls)); GNUNET_assert (fcls.n == 1); LOG (GNUNET_ERROR_TYPE_INFO, "message_get_fragment()\n"); fcls.n = 0; GNUNET_assert ( GNUNET_OK == db->message_get_fragment (db->cls, &channel_pub_key, GNUNET_ntohll (msg->message_id), GNUNET_ntohll (msg->fragment_offset), fragment_cb, &fcls)); GNUNET_assert (fcls.n == 1); LOG (GNUNET_ERROR_TYPE_INFO, "message_add_flags()\n"); GNUNET_assert ( GNUNET_OK == db->message_add_flags (db->cls, &channel_pub_key, GNUNET_ntohll (msg->message_id), GNUNET_PSYCSTORE_MESSAGE_STATE_APPLIED)); LOG (GNUNET_ERROR_TYPE_INFO, "fragment_get(%" PRIu64 ")\n", fragment_id); fcls.n = 0; fcls.flags[0] |= GNUNET_PSYCSTORE_MESSAGE_STATE_APPLIED; GNUNET_assert ( GNUNET_OK == db->fragment_get (db->cls, &channel_pub_key, fragment_id, fragment_id, &ret_frags, fragment_cb, &fcls)); GNUNET_assert (fcls.n == 1); LOG (GNUNET_ERROR_TYPE_INFO, "fragment_store()\n"); struct GNUNET_MULTICAST_MessageHeader *msg1 = GNUNET_malloc (sizeof (*msg1) + sizeof (channel_pub_key)); GNUNET_memcpy (msg1, msg, sizeof (*msg1) + sizeof (channel_pub_key)); msg1->fragment_id = GNUNET_htonll (INT64_MAX); msg1->fragment_offset = GNUNET_htonll (32768); fcls.n = 0; fcls.msg[1] = msg1; fcls.flags[1] = GNUNET_PSYCSTORE_MESSAGE_STATE_HASH; GNUNET_assert (GNUNET_OK == db->fragment_store (db->cls, &channel_pub_key, msg1, fcls.flags[1])); LOG (GNUNET_ERROR_TYPE_INFO, "message_get()\n"); GNUNET_assert ( GNUNET_OK == db->message_get (db->cls, &channel_pub_key, message_id, message_id, 0, &ret_frags, fragment_cb, &fcls)); GNUNET_assert (fcls.n == 2 && ret_frags == 2); /* Message counters */ LOG (GNUNET_ERROR_TYPE_INFO, "counters_message_get()\n"); fragment_id = 0; message_id = 0; group_generation = 0; GNUNET_assert ( GNUNET_OK == db->counters_message_get (db->cls, &channel_pub_key, &fragment_id, &message_id, &group_generation) && fragment_id == GNUNET_ntohll (msg1->fragment_id) && message_id == GNUNET_ntohll (msg1->message_id) && group_generation == GNUNET_ntohll (msg1->group_generation)); /* Modify state */ LOG (GNUNET_ERROR_TYPE_INFO, "STATE\n"); LOG (GNUNET_ERROR_TYPE_INFO, "state_modify_*()\n"); message_id = GNUNET_ntohll (fcls.msg[0]->message_id) + 1; GNUNET_assert (GNUNET_OK == db->state_modify_begin (db->cls, &channel_pub_key, message_id, 0)); GNUNET_assert (GNUNET_OK == db->state_modify_op (db->cls, &channel_pub_key, GNUNET_PSYC_OP_ASSIGN, "_foo", C2ARG("one two three"))); GNUNET_assert (GNUNET_OK == db->state_modify_op (db->cls, &channel_pub_key, GNUNET_PSYC_OP_ASSIGN, "_foo_bar", slave_key, sizeof (*slave_key))); GNUNET_assert (GNUNET_OK == db->state_modify_end (db->cls, &channel_pub_key, message_id)); LOG (GNUNET_ERROR_TYPE_INFO, "state_get()\n"); struct StateClosure scls = { 0 }; scls.n = 0; scls.value[0] = "one two three"; scls.value_size[0] = strlen ("one two three"); GNUNET_assert (GNUNET_OK == db->state_get (db->cls, &channel_pub_key, "_foo", state_cb, &scls)); GNUNET_assert (scls.n == 1); LOG (GNUNET_ERROR_TYPE_INFO, "state_get_prefix()\n"); scls.n = 0; scls.value[1] = slave_key; scls.value_size[1] = sizeof (*slave_key); GNUNET_assert (GNUNET_OK == db->state_get_prefix (db->cls, &channel_pub_key, "_foo", state_cb, &scls)); GNUNET_assert (scls.n == 2); LOG (GNUNET_ERROR_TYPE_INFO, "state_get_signed()\n"); scls.n = 0; GNUNET_assert (GNUNET_NO == db->state_get_signed (db->cls, &channel_pub_key, state_cb, &scls)); GNUNET_assert (scls.n == 0); LOG (GNUNET_ERROR_TYPE_INFO, "state_update_signed()\n"); GNUNET_assert (GNUNET_OK == db->state_update_signed (db->cls, &channel_pub_key)); LOG (GNUNET_ERROR_TYPE_INFO, "state_get_signed()\n"); scls.n = 0; GNUNET_assert (GNUNET_YES == db->state_get_signed (db->cls, &channel_pub_key, state_cb, &scls)); GNUNET_assert (scls.n == 2); /* State counters */ LOG (GNUNET_ERROR_TYPE_INFO, "counters_state_get()\n"); uint64_t max_state_msg_id = 0; GNUNET_assert (GNUNET_OK == db->counters_state_get (db->cls, &channel_pub_key, &max_state_msg_id) && max_state_msg_id == message_id); /* State sync */ LOG (GNUNET_ERROR_TYPE_INFO, "state_sync_*()\n"); scls.n = 0; scls.value[0] = channel_key; scls.value_size[0] = sizeof (*channel_key); scls.value[1] = "three two one"; scls.value_size[1] = strlen ("three two one"); GNUNET_assert (GNUNET_OK == db->state_sync_begin (db->cls, &channel_pub_key)); GNUNET_assert (GNUNET_OK == db->state_sync_assign (db->cls, &channel_pub_key, "_sync_bar", scls.value[0], scls.value_size[0])); GNUNET_assert (GNUNET_OK == db->state_sync_assign (db->cls, &channel_pub_key, "_sync_foo", scls.value[1], scls.value_size[1])); GNUNET_assert (GNUNET_OK == db->state_sync_end (db->cls, &channel_pub_key, max_state_msg_id, INT64_MAX - 5)); GNUNET_assert (GNUNET_NO == db->state_get_prefix (db->cls, &channel_pub_key, "_foo", state_cb, &scls)); GNUNET_assert (scls.n == 0); GNUNET_assert (GNUNET_OK == db->state_get_prefix (db->cls, &channel_pub_key, "_sync", state_cb, &scls)); GNUNET_assert (scls.n == 2); scls.n = 0; GNUNET_assert (GNUNET_OK == db->state_get_signed (db->cls, &channel_pub_key, state_cb, &scls)); GNUNET_assert (scls.n == 2); /* Modify state after sync */ LOG (GNUNET_ERROR_TYPE_INFO, "state_modify_*()\n"); message_id = GNUNET_ntohll (fcls.msg[0]->message_id) + 6; GNUNET_assert (GNUNET_OK == db->state_modify_begin (db->cls, &channel_pub_key, message_id, message_id - max_state_msg_id)); GNUNET_assert (GNUNET_OK == db->state_modify_op (db->cls, &channel_pub_key, GNUNET_PSYC_OP_ASSIGN, "_sync_foo", C2ARG("five six seven"))); GNUNET_assert (GNUNET_OK == db->state_modify_end (db->cls, &channel_pub_key, message_id)); /* Reset state */ LOG (GNUNET_ERROR_TYPE_INFO, "state_reset()\n"); scls.n = 0; GNUNET_assert (GNUNET_OK == db->state_reset (db->cls, &channel_pub_key)); GNUNET_assert (scls.n == 0); ok = 0; if (NULL != channel_key) { GNUNET_free (channel_key); channel_key = NULL; } if (NULL != slave_key) { GNUNET_free (slave_key); slave_key = NULL; } unload_plugin (db); } int main (int argc, char *argv[]) { char cfg_name[128]; char *const xargv[] = { "test-plugin-psycstore", "-c", cfg_name, "-L", LOG_LEVEL, NULL }; struct GNUNET_GETOPT_CommandLineOption options[] = { GNUNET_GETOPT_OPTION_END }; GNUNET_DISK_directory_remove ("/tmp/gnunet-test-plugin-psycstore-sqlite"); GNUNET_log_setup ("test-plugin-psycstore", LOG_LEVEL, NULL); plugin_name = GNUNET_TESTING_get_testname_from_underscore (argv[0]); GNUNET_snprintf (cfg_name, sizeof (cfg_name), "test_plugin_psycstore_%s.conf", plugin_name); GNUNET_PROGRAM_run ((sizeof (xargv) / sizeof (char *)) - 1, xargv, "test-plugin-psycstore", "nohelp", options, &run, NULL); if ( (0 != ok) && (77 != ok) ) fprintf (stderr, "Missed some testcases: %d\n", ok); #if ! DEBUG_PSYCSTORE GNUNET_DISK_directory_remove ("/tmp/gnunet-test-plugin-psycstore-sqlite"); #endif return ok; } /* end of test_plugin_psycstore.c */