From 4c1bf6607060cea867fccf667063c028dfd51e96 Mon Sep 17 00:00:00 2001 From: Stephen Gallagher Date: Wed, 4 Jan 2012 14:22:22 -0500 Subject: SYSDB: Add sysdb routines for manipulating service entries --- src/db/sysdb_services.c | 692 ++++++++++++++++++++++++++++++++++++++++++++++++ src/db/sysdb_services.h | 81 ++++++ src/tests/sysdb-tests.c | 358 +++++++++++++++++++++++++ 3 files changed, 1131 insertions(+) create mode 100644 src/db/sysdb_services.c create mode 100644 src/db/sysdb_services.h (limited to 'src') diff --git a/src/db/sysdb_services.c b/src/db/sysdb_services.c new file mode 100644 index 000000000..efdf7f11e --- /dev/null +++ b/src/db/sysdb_services.c @@ -0,0 +1,692 @@ +/* + SSSD + + Authors: + Stephen Gallagher + + Copyright (C) 2012 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + + +#include "util/util.h" +#include "db/sysdb.h" +#include "db/sysdb_private.h" +#include "db/sysdb_services.h" + +errno_t +sysdb_svc_add(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + const char *primary_name, + int port, + const char **aliases, + const char **protocols, + struct ldb_dn **dn); + +static errno_t +sysdb_svc_update(struct sysdb_ctx *sysdb, + struct ldb_dn *dn, + int port, + const char **aliases, + const char **protocols); + +errno_t +sysdb_svc_remove_alias(struct sysdb_ctx *sysdb, + struct ldb_dn *dn, + const char *alias); + +errno_t +sysdb_getservbyname(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + const char *name, + const char *proto, + struct ldb_result **_res) +{ + errno_t ret; + int lret; + TALLOC_CTX *tmp_ctx; + static const char *attrs[] = SYSDB_SVC_ATTRS; + struct ldb_dn *base_dn; + struct ldb_result *res; + char *sanitized_name; + char *sanitized_proto; + + *_res = NULL; + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + return ENOMEM; + } + + base_dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, + SYSDB_TMPL_SVC_BASE, + sysdb->domain->name); + if (!base_dn) { + ret = ENOMEM; + goto done; + } + + ret = sss_filter_sanitize(tmp_ctx, name, &sanitized_name); + if (ret != EOK) { + goto done; + } + + if (proto) { + ret = sss_filter_sanitize(tmp_ctx, proto, &sanitized_proto); + if (ret != EOK) { + goto done; + } + } + + lret = ldb_search(sysdb->ldb, tmp_ctx, &res, base_dn, + LDB_SCOPE_SUBTREE, attrs, + SYSDB_SVC_BYNAME_FILTER, + proto?sanitized_proto:"*", + sanitized_name, sanitized_name); + if (lret != LDB_SUCCESS) { + ret = sysdb_error_to_errno(lret); + goto done; + } + + if (res->count == 0) { + ret = ENOENT; + goto done; + } + + *_res = talloc_steal(mem_ctx, res); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +errno_t +sysdb_getservbyport(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + int port, + const char *proto, + struct ldb_result **_res) +{ + errno_t ret; + int lret; + TALLOC_CTX *tmp_ctx; + static const char *attrs[] = SYSDB_SVC_ATTRS; + struct ldb_dn *base_dn; + struct ldb_result *res; + char *sanitized_proto = NULL; + + if (port <= 0) { + return EINVAL; + } + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + return ENOMEM; + } + + base_dn = ldb_dn_new_fmt(tmp_ctx, sysdb->ldb, + SYSDB_TMPL_SVC_BASE, + sysdb->domain->name); + if (!base_dn) { + ret = ENOMEM; + goto done; + } + + if (proto) { + ret = sss_filter_sanitize(tmp_ctx, proto, &sanitized_proto); + if (ret != EOK) { + goto done; + } + } + + lret = ldb_search(sysdb->ldb, tmp_ctx, &res, base_dn, + LDB_SCOPE_SUBTREE, attrs, + SYSDB_SVC_BYPORT_FILTER, + sanitized_proto?sanitized_proto:"*", + (unsigned int) port); + if (lret) { + ret = sysdb_error_to_errno(lret); + goto done; + } + + if (res->count == 0) { + ret = ENOENT; + goto done; + } + + *_res = talloc_steal(mem_ctx, res); + + ret = EOK; + +done: + talloc_free(tmp_ctx); + return ret; +} + +errno_t +sysdb_store_service(struct sysdb_ctx *sysdb, + const char *primary_name, + int port, + const char **aliases, + const char **protocols, + uint64_t cache_timeout, + time_t now) +{ + errno_t ret; + errno_t sret; + TALLOC_CTX *tmp_ctx; + bool in_transaction = false; + struct ldb_result *res = NULL; + const char *name; + unsigned int i; + struct ldb_dn *update_dn = NULL; + struct sysdb_attrs *attrs; + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) return ENOMEM; + + ret = sysdb_transaction_start(sysdb); + if (ret != EOK) goto done; + + in_transaction = true; + + /* Check that the port is unique + * If the port appears for any service other than + * the one matching the primary_name, we need to + * remove them so that getservbyport() can work + * properly. Last entry saved to the cache should + * always "win". + */ + ret = sysdb_getservbyport(tmp_ctx, sysdb, port, NULL, &res); + if (ret != EOK && ret != ENOENT) { + goto done; + } else if (ret != ENOENT) { + if (res->count != 1) { + /* Somehow the cache has multiple entries with + * the same port. This is corrupted. We'll delete + * them all to sort it out. + */ + for (i = 0; i < res->count; i++) { + DEBUG(SSSDBG_TRACE_FUNC, + ("Corrupt cache entry [%s] detected. Deleting\n", + ldb_dn_canonical_string(tmp_ctx, + res->msgs[i]->dn))); + + ret = sysdb_delete_entry(sysdb, res->msgs[i]->dn, true); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Could not delete corrupt cache entry [%s]\n", + ldb_dn_canonical_string(tmp_ctx, + res->msgs[i]->dn))); + goto done; + } + } + } else { + /* Check whether this is the same name as we're currently + * saving to the cache. + */ + name = ldb_msg_find_attr_as_string(res->msgs[0], + SYSDB_NAME, + NULL); + if (!name || strcmp(name, primary_name) != 0) { + + if (!name) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("A service with no name?\n")); + /* Corrupted */ + } + + /* Either this is a corrupt entry or it's another service + * claiming ownership of this port. In order to account + * for port reassignments, we need to delete the old entry. + */ + DEBUG(SSSDBG_TRACE_FUNC, + ("Corrupt or replaced cache entry [%s] detected. " + "Deleting\n", + ldb_dn_canonical_string(tmp_ctx, + res->msgs[0]->dn))); + + ret = sysdb_delete_entry(sysdb, res->msgs[0]->dn, true); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Could not delete cache entry [%s]\n", + ldb_dn_canonical_string(tmp_ctx, + res->msgs[0]->dn))); + } + } + } + } + talloc_zfree(res); + + /* Ok, ports should now be unique. Now look + * the service up by name to determine if we + * need to update existing entries or modify + * aliases. + */ + ret = sysdb_getservbyname(tmp_ctx, sysdb, primary_name, NULL, &res); + if (ret != EOK && ret != ENOENT) { + goto done; + } else if (ret != ENOENT) { /* Found entries */ + for (i = 0; i < res->count; i++) { + /* Check whether this is the same name as we're currently + * saving to the cache. + */ + name = ldb_msg_find_attr_as_string(res->msgs[i], + SYSDB_NAME, + NULL); + if (!name) { + + /* Corrupted */ + DEBUG(SSSDBG_CRIT_FAILURE, + ("A service with no name?\n")); + DEBUG(SSSDBG_TRACE_FUNC, + ("Corrupt cache entry [%s] detected. Deleting\n", + ldb_dn_canonical_string(tmp_ctx, + res->msgs[i]->dn))); + + ret = sysdb_delete_entry(sysdb, res->msgs[i]->dn, true); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Could not delete corrupt cache entry [%s]\n", + ldb_dn_canonical_string(tmp_ctx, + res->msgs[i]->dn))); + goto done; + } + } else if (strcmp(name, primary_name) == 0) { + /* This is the same service name, so we need + * to update this entry with the values + * provided. + */ + if(update_dn) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Two existing services with the same name: [%s]? " + "Deleting both.\n", + primary_name)); + + /* Delete the entry from the previous pass */ + ret = sysdb_delete_entry(sysdb, update_dn, true); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Could not delete cache entry [%s]\n", + ldb_dn_canonical_string(tmp_ctx, + update_dn))); + goto done; + } + + /* Delete the new entry as well */ + ret = sysdb_delete_entry(sysdb, res->msgs[i]->dn, true); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + ("Could not delete cache entry [%s]\n", + ldb_dn_canonical_string(tmp_ctx, + res->msgs[i]->dn))); + goto done; + } + + update_dn = NULL; + } else { + update_dn = talloc_steal(tmp_ctx, res->msgs[i]->dn); + } + } else { + /* Another service is claiming this name as an alias. + * In order to account for aliases being promoted to + * primary names, we need to make sure to remove the + * old alias entry. + */ + ret = sysdb_svc_remove_alias(sysdb, + res->msgs[i]->dn, + primary_name); + if (ret != EOK) goto done; + } + } + talloc_zfree(res); + } + + if (update_dn) { + /* Update the existing entry */ + ret = sysdb_svc_update(sysdb, update_dn, port, aliases, protocols); + } else { + /* Add a new entry */ + ret = sysdb_svc_add(tmp_ctx, sysdb, primary_name, port, + aliases, protocols, &update_dn); + } + if (ret != EOK) goto done; + + /* Set the cache timeout */ + attrs = sysdb_new_attrs(tmp_ctx); + if (!attrs) { + ret = ENOMEM; + goto done; + } + ret = sysdb_attrs_add_time_t(attrs, SYSDB_LAST_UPDATE, now); + if (ret) goto done; + + ret = sysdb_attrs_add_time_t(attrs, SYSDB_CACHE_EXPIRE, + ((cache_timeout) ? + (now + cache_timeout) : 0)); + if (ret) goto done; + + ret = sysdb_set_entry_attr(sysdb, update_dn, attrs, SYSDB_MOD_REP); + if (ret != EOK) goto done; + + ret = sysdb_transaction_commit(sysdb); + if (ret == EOK) in_transaction = false; + +done: + if (in_transaction) { + sret = sysdb_transaction_cancel(sysdb); + if (sret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Could not cancel transaction\n")); + } + } + talloc_free(tmp_ctx); + return ret; +} + +struct ldb_dn * +sysdb_svc_dn(struct sysdb_ctx *sysdb, TALLOC_CTX *mem_ctx, + const char *domain, const char *name) +{ + errno_t ret; + char *clean_name; + struct ldb_dn *dn; + + ret = sysdb_dn_sanitize(NULL, name, &clean_name); + if (ret != EOK) { + return NULL; + } + + dn = ldb_dn_new_fmt(mem_ctx, sysdb->ldb, SYSDB_TMPL_SVC, + clean_name, domain); + talloc_free(clean_name); + + return dn; +} + +errno_t +sysdb_svc_add(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + const char *primary_name, + int port, + const char **aliases, + const char **protocols, + struct ldb_dn **dn) +{ + errno_t ret; + int lret; + TALLOC_CTX *tmp_ctx; + struct ldb_message *msg; + unsigned long i; + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) return ENOMEM; + + msg = ldb_msg_new(tmp_ctx); + if (!msg) { + ret = ENOMEM; + goto done; + } + + /* svc dn */ + msg->dn = sysdb_svc_dn(sysdb, msg, sysdb->domain->name, primary_name); + if (!msg->dn) { + ret = ENOMEM; + goto done; + } + + /* Objectclass */ + ret = add_string(msg, LDB_FLAG_MOD_ADD, + SYSDB_OBJECTCLASS, SYSDB_SVC_CLASS); + if (ret != EOK) goto done; + + /* Set the primary name */ + ret = add_string(msg, LDB_FLAG_MOD_ADD, + SYSDB_NAME, primary_name); + if (ret != EOK) goto done; + + /* Set the port number */ + ret = add_ulong(msg, LDB_FLAG_MOD_ADD, + SYSDB_SVC_PORT, port); + if (ret != EOK) goto done; + + /* If this service has any aliases, include them */ + if (aliases && aliases[0]) { + /* Set the name aliases */ + lret = ldb_msg_add_empty(msg, SYSDB_NAME_ALIAS, + LDB_FLAG_MOD_ADD, NULL); + if (lret != LDB_SUCCESS) { + ret = sysdb_error_to_errno(lret); + goto done; + } + for (i=0; aliases[i]; i++) { + lret = ldb_msg_add_string(msg, SYSDB_NAME_ALIAS, aliases[i]); + if (lret != LDB_SUCCESS) { + ret = sysdb_error_to_errno(lret); + goto done; + } + } + } + + /* Set the protocols */ + lret = ldb_msg_add_empty(msg, SYSDB_SVC_PROTO, + LDB_FLAG_MOD_ADD, NULL); + if (lret != LDB_SUCCESS) { + ret = sysdb_error_to_errno(lret); + goto done; + } + for (i=0; protocols[i]; i++) { + lret = ldb_msg_add_string(msg, SYSDB_SVC_PROTO, protocols[i]); + if (lret != LDB_SUCCESS) { + ret = sysdb_error_to_errno(lret); + goto done; + } + } + + /* creation time */ + ret = add_ulong(msg, LDB_FLAG_MOD_ADD, SYSDB_CREATE_TIME, + (unsigned long)time(NULL)); + if (ret) goto done; + + lret = ldb_add(sysdb->ldb, msg); + ret = sysdb_error_to_errno(lret); + + if (ret == EOK && dn) { + *dn = talloc_steal(mem_ctx, msg->dn); + } + +done: + if (ret) { + DEBUG(SSSDBG_TRACE_INTERNAL, + ("Error: %d (%s)\n", ret, strerror(ret))); + } + talloc_free(tmp_ctx); + return ret; +} + +static errno_t +sysdb_svc_update(struct sysdb_ctx *sysdb, + struct ldb_dn *dn, + int port, + const char **aliases, + const char **protocols) +{ + errno_t ret; + struct ldb_message *msg; + int lret; + unsigned int i; + + if (!dn || !protocols || !protocols[0]) { + return EINVAL; + } + + msg = ldb_msg_new(NULL); + if (!msg) { + ret = ENOMEM; + goto done; + } + + msg->dn = dn; + + /* Update the port */ + ret = add_ulong(msg, SYSDB_MOD_REP, + SYSDB_SVC_PORT, port); + if (ret != EOK) goto done; + + if (aliases && aliases[0]) { + /* Update the aliases */ + lret = ldb_msg_add_empty(msg, SYSDB_NAME_ALIAS, SYSDB_MOD_REP, NULL); + if (lret != LDB_SUCCESS) { + ret = ENOMEM; + goto done; + } + + for (i = 0; aliases[i]; i++) { + lret = ldb_msg_add_fmt(msg, SYSDB_NAME_ALIAS, "%s", aliases[i]); + if (lret != LDB_SUCCESS) { + ret = EINVAL; + goto done; + } + } + } + + /* Update the protocols */ + lret = ldb_msg_add_empty(msg, SYSDB_SVC_PROTO, SYSDB_MOD_REP, NULL); + if (lret != LDB_SUCCESS) { + ret = ENOMEM; + goto done; + } + + for (i = 0; protocols[i]; i++) { + lret = ldb_msg_add_fmt(msg, SYSDB_SVC_PROTO, "%s", protocols[i]); + if (lret != LDB_SUCCESS) { + ret = EINVAL; + goto done; + } + } + + lret = ldb_modify(sysdb->ldb, msg); + ret = sysdb_error_to_errno(lret); + +done: + if (ret) { + DEBUG(SSSDBG_TRACE_INTERNAL, + ("Error: %d (%s)\n", ret, strerror(ret))); + } + talloc_free(msg); + return ret; +} + +errno_t +sysdb_svc_remove_alias(struct sysdb_ctx *sysdb, + struct ldb_dn *dn, + const char *alias) +{ + errno_t ret; + struct ldb_message *msg; + int lret; + + msg = ldb_msg_new(NULL); + if (!msg) { + ret = ENOMEM; + goto done; + } + + msg->dn = dn; + + ret = add_string(msg, SYSDB_MOD_DEL, + SYSDB_NAME_ALIAS, alias); + if (ret != EOK) goto done; + + lret = ldb_modify(sysdb->ldb, msg); + ret = sysdb_error_to_errno(lret); + +done: + if (ret) { + DEBUG(SSSDBG_TRACE_INTERNAL, + ("Error: %d (%s)\n", ret, strerror(ret))); + } + talloc_zfree(msg); + return ret; +} + +errno_t +sysdb_svc_delete(struct sysdb_ctx *sysdb, + const char *name, + int port, + const char *proto) +{ + errno_t ret, sret; + TALLOC_CTX *tmp_ctx; + struct ldb_result *res; + unsigned int i; + bool in_transaction; + + tmp_ctx = talloc_new(NULL); + if (!tmp_ctx) { + return ENOMEM; + } + + ret = sysdb_transaction_start(sysdb); + if (ret != EOK) goto done; + + in_transaction = true; + + if (name) { + ret = sysdb_getservbyname(tmp_ctx, sysdb, name, proto, &res); + if (ret != EOK && ret != ENOENT) goto done; + if (ret == ENOENT) { + /* Doesn't exist in the DB. Nothing to do */ + ret = EOK; + goto done; + } + } else { + ret = sysdb_getservbyport(tmp_ctx, sysdb, port, proto, &res); + if (ret != EOK && ret != ENOENT) goto done; + if (ret == ENOENT) { + /* Doesn't exist in the DB. Nothing to do */ + ret = EOK; + goto done; + } + } + + /* There should only be one matching entry, + * but if there are multiple, we should delete + * them all to de-corrupt the DB. + */ + for (i = 0; i < res->count; i++) { + ret = sysdb_delete_entry(sysdb, res->msgs[i]->dn, false); + if (ret != EOK) goto done; + } + + ret = sysdb_transaction_commit(sysdb); + in_transaction = false; + +done: + if (in_transaction) { + sret = sysdb_transaction_cancel(sysdb); + if (sret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Could not cancel transaction\n")); + } + } + if (ret != EOK && ret != ENOENT) { + DEBUG(SSSDBG_TRACE_INTERNAL, + ("Error: %d (%s)\n", ret, strerror(ret))); + } + talloc_zfree(tmp_ctx); + return ret; +} diff --git a/src/db/sysdb_services.h b/src/db/sysdb_services.h new file mode 100644 index 000000000..40612355f --- /dev/null +++ b/src/db/sysdb_services.h @@ -0,0 +1,81 @@ +/* + SSSD + + Authors: + Stephen Gallagher + + Copyright (C) 2012 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef SYSDB_SERVICES_H_ +#define SYSDB_SERVICES_H_ + +#define SYSDB_SVC_CLASS "service" +#define SYSDB_SVC_CONTAINER "cn=services" +#define SYSDB_SC "objectclass="SYSDB_SVC_CLASS + +#define SYSDB_SVC_PORT "servicePort" +#define SYSDB_SVC_PROTO "serviceProtocol" + +#define SYSDB_TMPL_SVC_BASE SYSDB_SVC_CONTAINER",cn=%s,"SYSDB_BASE +#define SYSDB_TMPL_SVC SYSDB_NAME"=%s,"SYSDB_TMPL_SVC_BASE + +#define SYSDB_SVC_BYNAME_FILTER "(&("SYSDB_SC")("SYSDB_SVC_PROTO"=%s)(|("SYSDB_NAME_ALIAS"=%s)("SYSDB_NAME"=%s)))" +#define SYSDB_SVC_BYPORT_FILTER "(&("SYSDB_SC")("SYSDB_SVC_PROTO"=%s)("SYSDB_SVC_PORT"=%u))" + + +#define SYSDB_SVC_ATTRS { \ + SYSDB_NAME, \ + SYSDB_NAME_ALIAS, \ + SYSDB_SVC_PORT, \ + SYSDB_SVC_PROTO, \ + SYSDB_DEFAULT_ATTRS, \ + NULL } + +errno_t +sysdb_getservbyname(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + const char *name, + const char *proto, + struct ldb_result **_res); + +errno_t +sysdb_getservbyport(TALLOC_CTX *mem_ctx, + struct sysdb_ctx *sysdb, + int port, + const char *proto, + struct ldb_result **_res); + +errno_t +sysdb_store_service(struct sysdb_ctx *sysdb, + const char *primary_name, + int port, + const char **aliases, + const char **protocols, + uint64_t cache_timeout, + time_t now); + +struct ldb_dn * +sysdb_svc_dn(struct sysdb_ctx *sysdb, TALLOC_CTX *mem_ctx, + const char *domain, const char *name); + +errno_t +sysdb_svc_delete(struct sysdb_ctx *sysdb, + const char *name, + int port, + const char *proto); + +#endif /* SYSDB_SERVICES_H_ */ diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c index 9c080c9e2..8b78ff320 100644 --- a/src/tests/sysdb-tests.c +++ b/src/tests/sysdb-tests.c @@ -29,6 +29,7 @@ #include "util/util.h" #include "confdb/confdb_setup.h" #include "db/sysdb_private.h" +#include "db/sysdb_services.h" #include "tests/common.h" #define TESTS_PATH "tests_sysdb" @@ -2811,6 +2812,356 @@ START_TEST(test_odd_characters) } END_TEST +/* == SERVICE TESTS == */ +void services_check_match(struct sysdb_test_ctx *test_ctx, + bool by_name, + const char *primary_name, + int port, + const char **aliases, + const char **protocols) +{ + errno_t ret; + unsigned int i, j; + bool matched; + const char *ret_name; + int ret_port; + struct ldb_result *res; + struct ldb_message *msg; + struct ldb_message_element *el; + + if (by_name) { + /* Look up the service by name */ + ret = sysdb_getservbyname(test_ctx, test_ctx->sysdb, primary_name, + NULL, &res); + fail_if(ret != EOK, "sysdb_getservbyname error [%s]\n", + strerror(ret)); + } else { + /* Look up the newly-added service by port */ + ret = sysdb_getservbyport(test_ctx, test_ctx->sysdb, + port, NULL, &res); + fail_if(ret != EOK, "sysdb_getservbyport error [%s]\n", + strerror(ret)); + } + fail_if(res == NULL, "ENOMEM"); + fail_if(res->count != 1); + + /* Make sure the returned entry matches */ + msg = res->msgs[0]; + ret_name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL); + fail_if(ret_name == NULL); + fail_unless(strcmp(ret_name, primary_name) == 0); + + ret_port = ldb_msg_find_attr_as_int(msg, SYSDB_SVC_PORT, 0); + fail_if (ret_port != port); + + el = ldb_msg_find_element(msg, SYSDB_NAME_ALIAS); + for (i = 0; i < el->num_values; i++) { + matched = false; + for (j = 0; aliases[j]; j++) { + if (strcmp(aliases[j], (const char *)el->values[i].data) == 0) { + matched = true; + } + } + fail_if(!matched, "Unexpected value in LDB entry: [%s]", + (const char *)el->values[i].data); + } + + el = ldb_msg_find_element(msg, SYSDB_SVC_PROTO); + for (i = 0; i < el->num_values; i++) { + matched = false; + for (j = 0; protocols[j]; j++) { + if (strcmp(protocols[j], (const char *)el->values[i].data) == 0) { + matched = true; + } + } + fail_if(!matched, "Unexpected value in LDB entry: [%s]", + (const char *)el->values[i].data); + } +} + +#define services_check_match_name(test_ctx, primary_name, port, aliases, protocols) \ + do { \ + services_check_match(test_ctx, true, primary_name, port, aliases, protocols); \ + } while(0); + +#define services_check_match_port(test_ctx, primary_name, port, aliases, protocols) \ + do { \ + services_check_match(test_ctx, false, primary_name, port, aliases, protocols); \ + } while(0); + +START_TEST(test_sysdb_add_services) +{ + errno_t ret; + struct sysdb_test_ctx *test_ctx; + char *primary_name; + const char **aliases; + const char **protocols; + int port = 3890; + + /* Setup */ + ret = setup_sysdb_tests(&test_ctx); + fail_if(ret != EOK, "Could not set up the test"); + + primary_name = talloc_asprintf(test_ctx, "test_service"); + fail_if(primary_name == NULL); + + aliases = talloc_array(test_ctx, const char *, 3); + fail_if(aliases == NULL); + + aliases[0] = talloc_asprintf(aliases, "test_service_alias1"); + fail_if(aliases[0] == NULL); + + aliases[1] = talloc_asprintf(aliases, "test_service_alias2"); + fail_if(aliases[1] == NULL); + + aliases[2] = NULL; + + protocols = talloc_array(test_ctx, const char *, 3); + fail_if(protocols == NULL); + + protocols[0] = talloc_asprintf(protocols, "tcp"); + fail_if(protocols[0] == NULL); + + protocols[1] = talloc_asprintf(protocols, "udp"); + fail_if(protocols[1] == NULL); + + protocols[2] = NULL; + + ret = sysdb_transaction_start(test_ctx->sysdb); + fail_if(ret != EOK); + + ret = sysdb_svc_add(NULL, test_ctx->sysdb, + primary_name, port, + aliases, protocols, + NULL); + fail_unless(ret == EOK, "sysdb_svc_add error [%s]\n", strerror(ret)); + + /* Search by name and make sure the results match */ + services_check_match_name(test_ctx, + primary_name, port, + aliases, protocols); + + /* Search by port and make sure the results match */ + services_check_match_port(test_ctx, + primary_name, port, + aliases, protocols); + + ret = sysdb_transaction_commit(test_ctx->sysdb); + fail_if (ret != EOK); + + /* Clean up after ourselves (and test deleting by name) + * + * We have to do this after the transaction, because LDB + * doesn't like adding and deleting the same entry in a + * single transaction. + */ + ret = sysdb_svc_delete(test_ctx->sysdb, primary_name, 0, NULL); + fail_if(ret != EOK); + + talloc_free(test_ctx); +} +END_TEST + +START_TEST(test_sysdb_store_services) +{ + errno_t ret; + struct sysdb_test_ctx *test_ctx; + const char *primary_name = "test_store_service"; + const char *alt_primary_name = "alt_test_store_service"; + const char **aliases; + const char **protocols; + int port = 3890; + int altport = 3891; + + /* Setup */ + ret = setup_sysdb_tests(&test_ctx); + fail_if(ret != EOK, "Could not set up the test"); + + aliases = talloc_array(test_ctx, const char *, 3); + fail_if(aliases == NULL); + + aliases[0] = talloc_asprintf(aliases, "test_service_alias1"); + fail_if(aliases[0] == NULL); + + aliases[1] = talloc_asprintf(aliases, "test_service_alias2"); + fail_if(aliases[1] == NULL); + + aliases[2] = NULL; + + protocols = talloc_array(test_ctx, const char *, 3); + fail_if(protocols == NULL); + + protocols[0] = talloc_asprintf(protocols, "tcp"); + fail_if(protocols[0] == NULL); + + protocols[1] = talloc_asprintf(protocols, "udp"); + fail_if(protocols[1] == NULL); + + protocols[2] = NULL; + + ret = sysdb_transaction_start(test_ctx->sysdb); + fail_if(ret != EOK); + + /* Store this group (which will add it) */ + ret = sysdb_store_service(test_ctx->sysdb, + primary_name, port, + aliases, protocols, + 1, 1); + fail_if (ret != EOK); + + /* Search by name and make sure the results match */ + services_check_match_name(test_ctx, + primary_name, port, + aliases, protocols); + + /* Search by port and make sure the results match */ + services_check_match_port(test_ctx, + primary_name, port, + aliases, protocols); + + /* Change the service name */ + ret = sysdb_store_service(test_ctx->sysdb, + alt_primary_name, port, + aliases, protocols, + 1, 1); + fail_if (ret != EOK, "[%s]", strerror(ret)); + + services_check_match_name(test_ctx, + alt_primary_name, port, + aliases, protocols); + + /* Search by port and make sure the results match */ + services_check_match_port(test_ctx, + alt_primary_name, port, + aliases, protocols); + + + /* Change it back */ + ret = sysdb_store_service(test_ctx->sysdb, + primary_name, port, + aliases, protocols, + 1, 1); + fail_if (ret != EOK, "[%s]", strerror(ret)); + + /* Change the port number */ + ret = sysdb_store_service(test_ctx->sysdb, + primary_name, altport, + aliases, protocols, + 1, 1); + fail_if (ret != EOK, "[%s]", strerror(ret)); + + /* Search by name and make sure the results match */ + services_check_match_name(test_ctx, + primary_name, altport, + aliases, protocols); + + /* Search by port and make sure the results match */ + services_check_match_port(test_ctx, + primary_name, altport, + aliases, protocols); + + /* TODO: Test changing aliases and protocols */ + + ret = sysdb_transaction_commit(test_ctx->sysdb); + fail_if(ret != EOK, "[%s]", strerror(ret)); + + /* Clean up after ourselves (and test deleting by port) + * + * We have to do this after the transaction, because LDB + * doesn't like adding and deleting the same entry in a + * single transaction. + */ + ret = sysdb_svc_delete(test_ctx->sysdb, NULL, altport, NULL); + fail_if(ret != EOK); + + talloc_free(test_ctx); +} +END_TEST + +START_TEST(test_sysdb_svc_remove_alias) +{ + errno_t ret; + struct sysdb_test_ctx *test_ctx; + const char *primary_name = "remove_alias_test"; + const char **aliases; + const char **protocols; + int port = 3990; + struct ldb_dn *dn; + + /* Setup */ + ret = setup_sysdb_tests(&test_ctx); + fail_if(ret != EOK, "Could not set up the test"); + + aliases = talloc_array(test_ctx, const char *, 3); + fail_if(aliases == NULL); + + aliases[0] = talloc_asprintf(aliases, "remove_alias_alias1"); + fail_if(aliases[0] == NULL); + + aliases[1] = talloc_asprintf(aliases, "remove_alias_alias2"); + fail_if(aliases[1] == NULL); + + aliases[2] = NULL; + + protocols = talloc_array(test_ctx, const char *, 3); + fail_if(protocols == NULL); + + protocols[0] = talloc_asprintf(protocols, "tcp"); + fail_if(protocols[0] == NULL); + + protocols[1] = talloc_asprintf(protocols, "udp"); + fail_if(protocols[1] == NULL); + + protocols[2] = NULL; + + ret = sysdb_transaction_start(test_ctx->sysdb); + fail_if(ret != EOK); + + ret = sysdb_svc_add(NULL, test_ctx->sysdb, + primary_name, port, + aliases, protocols, + NULL); + fail_unless(ret == EOK, "sysdb_svc_add error [%s]\n", strerror(ret)); + + /* Search by name and make sure the results match */ + services_check_match_name(test_ctx, + primary_name, port, + aliases, protocols); + + /* Search by port and make sure the results match */ + services_check_match_port(test_ctx, + primary_name, port, + aliases, protocols); + + /* Now remove an alias */ + dn = sysdb_svc_dn(test_ctx->sysdb, test_ctx, test_ctx->domain->name, primary_name); + fail_if (dn == NULL); + + ret = sysdb_svc_remove_alias(test_ctx->sysdb, dn, aliases[1]); + fail_if (ret != EOK, "[%s]", strerror(ret)); + sysdb_transaction_commit(test_ctx->sysdb); + sysdb_transaction_start(test_ctx->sysdb); + + /* Set aliases[1] to NULL to perform validation checks */ + aliases[1] = NULL; + + /* Search by name and make sure the results match */ + services_check_match_name(test_ctx, + primary_name, port, + aliases, protocols); + + /* Search by port and make sure the results match */ + services_check_match_port(test_ctx, + primary_name, port, + aliases, protocols); + + ret = sysdb_transaction_commit(test_ctx->sysdb); + fail_if(ret != EOK); + + talloc_free(test_ctx); +} +END_TEST + START_TEST(test_sysdb_has_enumerated) { errno_t ret; @@ -3052,6 +3403,13 @@ Suite *create_sysdb_suite(void) /* Remove the other half by DN */ tcase_add_loop_test(tc_sysdb, test_sysdb_remove_netgroup_entry, 27005, 27010); +/* ===== SERVICE TESTS ===== */ + + /* Create a new service */ + tcase_add_test(tc_sysdb, test_sysdb_add_services); + tcase_add_test(tc_sysdb, test_sysdb_store_services); + tcase_add_test(tc_sysdb, test_sysdb_svc_remove_alias); + /* Add all test cases to the test suite */ suite_add_tcase(s, tc_sysdb); -- cgit