From 269ca19f13d9b297b7c86042ecdfc5698cdc8bc1 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 2 Dec 2021 11:25:34 +0100 Subject: [PATCH] metadata-set: Add implementation for a collection of metadata objects --- src/libstrongswan/Android.mk | 2 +- src/libstrongswan/Makefile.am | 3 +- src/libstrongswan/metadata/metadata_set.c | 180 ++++++++++++ src/libstrongswan/metadata/metadata_set.h | 78 +++++ src/libstrongswan/tests/Makefile.am | 3 +- .../tests/suites/test_metadata_set.c | 273 ++++++++++++++++++ src/libstrongswan/tests/tests.h | 1 + 7 files changed, 537 insertions(+), 3 deletions(-) create mode 100644 src/libstrongswan/metadata/metadata_set.c create mode 100644 src/libstrongswan/metadata/metadata_set.h create mode 100644 src/libstrongswan/tests/suites/test_metadata_set.c diff --git a/src/libstrongswan/Android.mk b/src/libstrongswan/Android.mk index be263efbe..660382da2 100644 --- a/src/libstrongswan/Android.mk +++ b/src/libstrongswan/Android.mk @@ -33,7 +33,7 @@ credentials/sets/cert_cache.c credentials/sets/mem_cred.c \ credentials/sets/callback_cred.c credentials/auth_cfg.c database/database.c \ database/database_factory.c fetcher/fetcher.c fetcher/fetcher_manager.c eap/eap.c \ ipsec/ipsec_types.c \ -metadata/metadata_factory.c metadata/metadata_int.c \ +metadata/metadata_factory.c metadata/metadata_int.c metadata/metadata_set.c \ networking/host.c networking/host_resolver.c networking/packet.c \ networking/tun_device.c networking/streams/stream_manager.c \ networking/streams/stream.c networking/streams/stream_service.c \ diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index ca3f1e1da..a12567291 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -31,7 +31,7 @@ credentials/sets/cert_cache.c credentials/sets/mem_cred.c \ credentials/sets/callback_cred.c credentials/auth_cfg.c database/database.c \ database/database_factory.c fetcher/fetcher.c fetcher/fetcher_manager.c eap/eap.c \ ipsec/ipsec_types.c \ -metadata/metadata_factory.c metadata/metadata_int.c \ +metadata/metadata_factory.c metadata/metadata_int.c metadata/metadata_set.c \ networking/host.c networking/host_resolver.c networking/packet.c \ networking/tun_device.c networking/streams/stream_manager.c \ networking/streams/stream.c networking/streams/stream_service.c \ @@ -101,6 +101,7 @@ credentials/auth_cfg.h credentials/credential_set.h credentials/cert_validator.h database/database.h database/database_factory.h fetcher/fetcher.h \ fetcher/fetcher_manager.h eap/eap.h pen/pen.h ipsec/ipsec_types.h \ metadata/metadata.h metadata/metadata_factory.h metadata/metadata_int.h \ +metadata/metadata_set.h \ networking/host.h networking/host_resolver.h networking/packet.h \ networking/tun_device.h networking/streams/stream.h \ networking/streams/stream_unix.h networking/streams/stream_service_unix.h \ diff --git a/src/libstrongswan/metadata/metadata_set.c b/src/libstrongswan/metadata/metadata_set.c new file mode 100644 index 000000000..f821e57c0 --- /dev/null +++ b/src/libstrongswan/metadata/metadata_set.c @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2021 Tobias Brunner, codelabs GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "metadata_set.h" + +#include + +/** + * Set of metadata objects, indexed via string. + */ +struct metadata_set_t { + /** Stored metadata objects (entry_t) */ + array_t *entries; +}; + +/** + * Stored data for each metadata object. + */ +typedef struct { + /** Key of the entry */ + const char *key; + /** Stored metadata object */ + metadata_t *data; +} entry_t; + +/** + * Destroy the given entry. + */ +static void destroy_entry(entry_t *entry) +{ + entry->data->destroy(entry->data); + free((char*)entry->key); + free(entry); +} + +/** + * Sort entries by key + */ +static int entry_sort(const void *a, const void *b, void *user) +{ + const entry_t *ea = a, *eb = b; + return strcmp(ea->key, eb->key); +} + +/** + * Find an entry by key + */ +static int entry_find(const void *a, const void *b) +{ + return entry_sort(a, b, NULL); +} + +/* + * Described in header + */ +metadata_set_t *metadata_set_create() +{ + metadata_set_t *set; + + INIT(set); + + return set; +} + +/* + * Described in header + */ +void metadata_set_put(metadata_set_t *set, const char *key, metadata_t *data) +{ + entry_t *entry = NULL, lookup = { + .key = key, + }; + int idx; + + if (!set) + { + DESTROY_IF(data); + return; + } + + idx = array_bsearch(set->entries, &lookup, entry_find, &entry); + if (idx != -1) + { + if (data) + { + entry->data->destroy(entry->data); + entry->data = data; + } + else + { + array_remove(set->entries, idx, NULL); + destroy_entry(entry); + } + } + else if (data) + { + INIT(entry, + .key = strdup(key), + .data = data, + ); + array_insert_create(&set->entries, ARRAY_TAIL, entry); + array_sort(set->entries, entry_sort, NULL); + } +} + +/* + * Described in header + */ +metadata_t *metadata_set_get(metadata_set_t *set, const char *key) +{ + entry_t *entry = NULL, lookup = { + .key = key, + }; + + if (set && array_bsearch(set->entries, &lookup, entry_find, &entry) != -1) + { + return entry->data; + } + return NULL; +} + +/* + * Described in header + */ +metadata_set_t *metadata_set_clone(metadata_set_t *set) +{ + metadata_set_t *clone; + entry_t *entry, *entry_clone; + int i; + + if (!set) + { + return NULL; + } + + INIT(clone, + .entries = array_create(0, array_count(set->entries)), + ); + for (i = 0; i < array_count(set->entries); i++) + { + array_get(set->entries, i, &entry); + INIT(entry_clone, + .key = strdup(entry->key), + .data = entry->data->clone(entry->data), + ); + array_insert(clone->entries, i, entry_clone); + } + return clone; +} + +/* + * Described in header + */ +void metadata_set_destroy(metadata_set_t *set) +{ + if (set) + { + array_destroy_function(set->entries, (void*)destroy_entry, NULL); + free(set); + } +} diff --git a/src/libstrongswan/metadata/metadata_set.h b/src/libstrongswan/metadata/metadata_set.h new file mode 100644 index 000000000..38ab51db5 --- /dev/null +++ b/src/libstrongswan/metadata/metadata_set.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2021 Tobias Brunner, codelabs GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * @defgroup metadata_set metadata_set + * @{ @ingroup metadata + */ + +#ifndef METADATA_SET_H_ +#define METADATA_SET_H_ + +#include "metadata.h" + +/** + * Set of metadata objects, indexed via string. + */ +typedef struct metadata_set_t metadata_set_t; + +/** + * Create a metadata_set_t instance. + */ +metadata_set_t *metadata_set_create(); + +/** + * Add a metadata object with the given key to the set, replacing any previous + * object with the same key. + * + * @param set set to add metadata to + * @param key key under which to store metadata (cloned) + * @param data metadata object (adopted), NULL to remove/destroy + * existing object + */ +void metadata_set_put(metadata_set_t *set, const char *key, metadata_t *data); + +/** + * Retrieve the metadata object with the given key. + * + * @param set set to query + * @param key key of the metadata object + * @return metadata object, NULL if not found + */ +metadata_t *metadata_set_get(metadata_set_t *set, const char *key); + +/** + * Clone a complete metadata set. + * + * @param set set to clone + * @return cloned set + */ +metadata_set_t *metadata_set_clone(metadata_set_t *set); + +/** + * Destroy a metadata set, destroying all contained metadata objects. + * + * @param set set to destroy + */ +void metadata_set_destroy(metadata_set_t *set); + +#endif /** METADATA_SET_H_ @}*/ diff --git a/src/libstrongswan/tests/Makefile.am b/src/libstrongswan/tests/Makefile.am index 37ea10772..eb9639676 100644 --- a/src/libstrongswan/tests/Makefile.am +++ b/src/libstrongswan/tests/Makefile.am @@ -66,7 +66,8 @@ libstrongswan_tests_SOURCES = tests.h tests.c \ suites/test_ed25519.c \ suites/test_ed448.c \ suites/test_signature_params.c \ - suites/test_metadata.c + suites/test_metadata.c \ + suites/test_metadata_set.c libstrongswan_tests_CFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ diff --git a/src/libstrongswan/tests/suites/test_metadata_set.c b/src/libstrongswan/tests/suites/test_metadata_set.c new file mode 100644 index 000000000..0a307c4c7 --- /dev/null +++ b/src/libstrongswan/tests/suites/test_metadata_set.c @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2021 Tobias Brunner, codelabs GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "test_suite.h" + +#include + +START_TEST(test_destroy_null) +{ + metadata_set_t *set = NULL; + metadata_set_destroy(set); +} +END_TEST + +START_TEST(test_destroy_empty) +{ + metadata_set_t *set = metadata_set_create(); + metadata_set_destroy(set); +} +END_TEST + +START_TEST(test_put_null) +{ + metadata_set_t *set = NULL; + metadata_t *metadata; + + metadata = lib->metadata->create(lib->metadata, "int", 42); + metadata_set_put(set, "key", metadata); + metadata_set_put(set, "other", NULL); +} +END_TEST + +START_TEST(test_put) +{ + metadata_set_t *set = metadata_set_create(); + metadata_t *metadata; + + metadata = lib->metadata->create(lib->metadata, "int", 42); + metadata_set_put(set, "key", metadata); + metadata_set_destroy(set); +} +END_TEST + +START_TEST(test_get_null) +{ + metadata_set_t *set = NULL; + metadata_t *metadata; + + metadata = metadata_set_get(set, "key"); + ck_assert(!metadata); +} +END_TEST + +/** + * Assert that the given int metadata value is found with the given key. + */ +static void assert_int_value(metadata_set_t *set, const char *key, int expected) +{ + metadata_t *metadata; + int value; + + metadata = metadata_set_get(set, key); + ck_assert(metadata); + metadata->get(metadata, &value); + ck_assert_int_eq(expected, value); +} + +START_TEST(test_get) +{ + metadata_set_t *set = metadata_set_create(); + metadata_t *metadata; + + metadata = lib->metadata->create(lib->metadata, "int", 42); + metadata_set_put(set, "key", metadata); + assert_int_value(set, "key", 42); + metadata_set_destroy(set); +} +END_TEST + +START_TEST(test_get_missing) +{ + metadata_set_t *set = metadata_set_create(); + metadata_t *metadata; + + metadata = metadata_set_get(set, "key"); + ck_assert(!metadata); + + metadata = lib->metadata->create(lib->metadata, "int", 42); + metadata_set_put(set, "other", metadata); + metadata = metadata_set_get(set, "key"); + ck_assert(!metadata); + metadata_set_destroy(set); +} +END_TEST + +START_TEST(test_get_multi) +{ + metadata_set_t *set = metadata_set_create(); + metadata_t *metadata; + + metadata = lib->metadata->create(lib->metadata, "int", 42); + metadata_set_put(set, "key", metadata); + metadata = lib->metadata->create(lib->metadata, "int", 0); + metadata_set_put(set, "other", metadata); + assert_int_value(set, "key", 42); + assert_int_value(set, "other", 0); + metadata_set_destroy(set); +} +END_TEST + +START_TEST(test_put_replace) +{ + metadata_set_t *set = metadata_set_create(); + metadata_t *metadata; + + metadata = lib->metadata->create(lib->metadata, "int", 42); + metadata_set_put(set, "whatever", metadata); + metadata = lib->metadata->create(lib->metadata, "int", 0); + metadata_set_put(set, "other", metadata); + metadata = lib->metadata->create(lib->metadata, "int", 666); + metadata_set_put(set, "other", metadata); + assert_int_value(set, "whatever", 42); + assert_int_value(set, "other", 666); + metadata_set_destroy(set); +} +END_TEST + +START_TEST(test_put_remove) +{ + metadata_set_t *set = metadata_set_create(); + metadata_t *metadata; + + metadata = lib->metadata->create(lib->metadata, "int", 42); + metadata_set_put(set, "key", metadata); + metadata = lib->metadata->create(lib->metadata, "int", 0); + metadata_set_put(set, "other", metadata); + metadata_set_put(set, "other", NULL); + assert_int_value(set, "key", 42); + metadata = metadata_set_get(set, "other"); + ck_assert(!metadata); + metadata_set_put(set, "key", NULL); + metadata = metadata_set_get(set, "key"); + ck_assert(!metadata); + metadata_set_destroy(set); +} +END_TEST + +START_TEST(test_put_remove_missing) +{ + metadata_set_t *set = metadata_set_create(); + metadata_t *metadata; + + metadata_set_put(set, "key", NULL); + metadata = lib->metadata->create(lib->metadata, "int", 42); + metadata_set_put(set, "key", metadata); + assert_int_value(set, "key", 42); + metadata_set_put(set, "key", NULL); + metadata = metadata_set_get(set, "key"); + ck_assert(!metadata); + metadata_set_put(set, "key", NULL); + metadata_set_destroy(set); +} +END_TEST + +START_TEST(test_clone_null) +{ + metadata_set_t *set = NULL, *clone; + + clone = metadata_set_clone(set); + ck_assert(!clone); +} +END_TEST + +START_TEST(test_clone_empty) +{ + metadata_set_t *set = metadata_set_create(), *clone; + + clone = metadata_set_clone(set); + ck_assert(clone != set); + + metadata_set_destroy(clone); + metadata_set_destroy(set); +} +END_TEST + +START_TEST(test_clone) +{ + metadata_set_t *set = metadata_set_create(), *clone; + metadata_t *metadata; + struct { + const char *key; + int value; + } expected[] = { + { "key", 42, }, + { "other", 666, }, + { "abc", 4500, }, + }; + int i; + + for (i = 0; i < countof(expected); i++) + { + metadata = lib->metadata->create(lib->metadata, "int", expected[i].value); + metadata_set_put(set, expected[i].key, metadata); + } + + clone = metadata_set_clone(set); + ck_assert(clone != set); + + for (i = 0; i < countof(expected); i++) + { + assert_int_value(set, expected[i].key, expected[i].value); + assert_int_value(clone, expected[i].key, expected[i].value); + } + + metadata_set_put(set, expected[0].key, NULL); + assert_int_value(clone, expected[0].key, expected[0].value); + + metadata_set_destroy(clone); + metadata_set_destroy(set); +} +END_TEST + +Suite *metadata_set_suite_create() +{ + Suite *s; + TCase *tc; + + s = suite_create("metadata_set"); + + tc = tcase_create("create/destroy"); + tcase_add_test(tc, test_destroy_null); + tcase_add_test(tc, test_destroy_empty); + suite_add_tcase(s, tc); + + tc = tcase_create("put/get"); + tcase_add_test(tc, test_put_null); + tcase_add_test(tc, test_put); + tcase_add_test(tc, test_get_null); + tcase_add_test(tc, test_get); + tcase_add_test(tc, test_get_missing); + tcase_add_test(tc, test_get_multi); + tcase_add_test(tc, test_put_replace); + tcase_add_test(tc, test_put_remove); + tcase_add_test(tc, test_put_remove_missing); + suite_add_tcase(s, tc); + + tc = tcase_create("clone"); + tcase_add_test(tc, test_clone_null); + tcase_add_test(tc, test_clone_empty); + tcase_add_test(tc, test_clone); + suite_add_tcase(s, tc); + + return s; +} diff --git a/src/libstrongswan/tests/tests.h b/src/libstrongswan/tests/tests.h index 7ff66d76b..fc685d193 100644 --- a/src/libstrongswan/tests/tests.h +++ b/src/libstrongswan/tests/tests.h @@ -62,3 +62,4 @@ TEST_SUITE_DEPEND(ed25519_suite_create, PRIVKEY_GEN, KEY_ED25519) TEST_SUITE_DEPEND(ed448_suite_create, PRIVKEY_GEN, KEY_ED448) TEST_SUITE(signature_params_suite_create) TEST_SUITE(metadata_suite_create) +TEST_SUITE(metadata_set_suite_create) -- 2.47.3