summaryrefslogtreecommitdiffstats
path: root/src/cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cache.c')
-rw-r--r--src/cache.c249
1 files changed, 249 insertions, 0 deletions
diff --git a/src/cache.c b/src/cache.c
new file mode 100644
index 0000000..fa57563
--- /dev/null
+++ b/src/cache.c
@@ -0,0 +1,249 @@
+/* Authors: Martin Nagy <mnagy@redhat.com>
+ *
+ * Copyright (C) 2009 Red Hat
+ * see file 'COPYING' for use and warranty information
+ *
+ * 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; version 2 only
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <isc/mem.h>
+#include <isc/mutex.h>
+#include <isc/result.h>
+#include <isc/time.h>
+#include <isc/util.h>
+
+#include <dns/rbt.h>
+#include <dns/result.h>
+
+#include <string.h>
+
+#include "cache.h"
+#include "ldap_helper.h"
+#include "log.h"
+#include "rdlist.h"
+#include "settings.h"
+#include "util.h"
+
+/* These macros require that variable 'is_locked' exists. */
+#define CONTROLED_LOCK(lock) \
+ do { \
+ LOCK(lock); \
+ is_locked = 1; \
+ } while (0)
+
+#define CONTROLED_UNLOCK(lock) \
+ do { \
+ if (is_locked) { \
+ UNLOCK(lock); \
+ is_locked = 0; \
+ } \
+ } while (0)
+
+struct ldap_cache {
+ isc_mutex_t mutex;
+ isc_mem_t *mctx;
+ dns_rbt_t *rbt;
+ isc_interval_t cache_ttl;
+};
+
+typedef struct {
+ isc_mem_t *mctx;
+ ldapdb_rdatalist_t rdatalist;
+ isc_time_t valid_until;
+} cache_node_t;
+
+static void
+cache_node_deleter(void *data, void *deleter_arg)
+{
+ cache_node_t *node = (cache_node_t *)data;
+
+ UNUSED(deleter_arg);
+ REQUIRE(data != NULL);
+
+ ldapdb_rdatalist_destroy(node->mctx, &node->rdatalist);
+ MEM_PUT_AND_DETACH(node);
+}
+
+static isc_result_t
+cache_node_create(ldap_cache_t *cache, ldapdb_rdatalist_t rdatalist,
+ cache_node_t **nodep)
+{
+ isc_result_t result;
+ cache_node_t *node;
+
+ REQUIRE(cache != NULL);
+ REQUIRE(nodep != NULL && *nodep == NULL);
+
+ CHECKED_MEM_GET_PTR(cache->mctx, node);
+ ZERO_PTR(node);
+ isc_mem_attach(cache->mctx, &node->mctx);
+ node->rdatalist = rdatalist;
+ CHECK(isc_time_nowplusinterval(&node->valid_until, &cache->cache_ttl));
+
+ *nodep = node;
+ return ISC_R_SUCCESS;
+
+cleanup:
+ SAFE_MEM_PUT_PTR(cache->mctx, node);
+
+ return result;
+}
+
+isc_result_t
+new_ldap_cache(isc_mem_t *mctx, ldap_cache_t **cachep,
+ const char * const *argv)
+{
+ isc_result_t result;
+ ldap_cache_t *cache = NULL;
+ unsigned int cache_ttl;
+ setting_t cache_settings[] = {
+ { "cache_ttl", default_uint(120) },
+ end_of_settings
+ };
+
+ REQUIRE(cachep != NULL && *cachep == NULL);
+
+ cache_settings[0].target = &cache_ttl;
+ CHECK(set_settings(cache_settings, argv));
+
+ CHECKED_MEM_GET_PTR(mctx, cache);
+ ZERO_PTR(cache);
+ isc_mem_attach(mctx, &cache->mctx);
+
+ isc_interval_set(&cache->cache_ttl, cache_ttl, 0);
+
+ if (cache_ttl) {
+ CHECK(dns_rbt_create(mctx, cache_node_deleter, NULL,
+ &cache->rbt));
+ CHECK(isc_mutex_init(&cache->mutex));
+ }
+
+ *cachep = cache;
+ return ISC_R_SUCCESS;
+
+cleanup:
+ if (cache != NULL)
+ destroy_ldap_cache(&cache);
+
+ return result;
+}
+
+void
+destroy_ldap_cache(ldap_cache_t **cachep)
+{
+ ldap_cache_t *cache;
+ int is_locked = 0;
+
+ REQUIRE(cachep != NULL && *cachep != NULL);
+
+ cache = *cachep;
+
+ if (cache->rbt) {
+ CONTROLED_LOCK(&cache->mutex);
+ dns_rbt_destroy(&cache->rbt);
+ cache->rbt = NULL;
+ CONTROLED_UNLOCK(&cache->mutex);
+ DESTROYLOCK(&cache->mutex);
+ }
+
+ MEM_PUT_AND_DETACH(cache);
+
+ *cachep = NULL;
+}
+
+isc_result_t
+cached_ldap_rdatalist_get(isc_mem_t *mctx, ldap_cache_t *cache,
+ ldap_db_t *ldap_db, dns_name_t *name,
+ ldapdb_rdatalist_t *rdatalist)
+{
+ isc_result_t result;
+ ldapdb_rdatalist_t rdlist;
+ cache_node_t *node = NULL;
+ int in_cache = 0;
+ int is_locked = 0;
+
+ REQUIRE(cache != NULL);
+
+ if (cache->rbt == NULL)
+ return ldapdb_rdatalist_get(mctx, ldap_db, name, rdatalist);
+
+ CONTROLED_LOCK(&cache->mutex);
+ result = dns_rbt_findname(cache->rbt, name, 0, NULL, (void *)&node);
+ if (result == ISC_R_SUCCESS) {
+ isc_time_t now;
+
+ CHECK(isc_time_now(&now));
+
+ /* Check if the record is still valid. */
+ if (isc_time_compare(&now, &node->valid_until) > 0) {
+ CHECK(dns_rbt_deletename(cache->rbt, name, ISC_FALSE));
+ in_cache = 0;
+ } else {
+ rdlist = node->rdatalist;
+ in_cache = 1;
+ }
+ } else if (result != ISC_R_NOTFOUND && result != DNS_R_PARTIALMATCH) {
+ goto cleanup;
+ }
+ CONTROLED_UNLOCK(&cache->mutex);
+
+ if (!in_cache) {
+ INIT_LIST(rdlist);
+ result = ldapdb_rdatalist_get(mctx, ldap_db, name, &rdlist);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
+ goto cleanup;
+ CONTROLED_LOCK(&cache->mutex);
+ /* Check again to make sure. */
+ node = NULL;
+ result = dns_rbt_findname(cache->rbt, name, 0, NULL,
+ (void *)&node);
+ if (result == ISC_R_NOTFOUND || result == DNS_R_PARTIALMATCH) {
+ node = NULL;
+ CHECK(cache_node_create(cache, rdlist, &node));
+ CHECK(dns_rbt_addname(cache->rbt, name, (void *)node));
+ }
+ CONTROLED_UNLOCK(&cache->mutex);
+ }
+
+ CHECK(ldap_rdatalist_copy(mctx, rdlist, rdatalist));
+
+ if (EMPTY(*rdatalist))
+ result = ISC_R_NOTFOUND;
+
+cleanup:
+ CONTROLED_UNLOCK(&cache->mutex);
+ return result;
+}
+
+isc_result_t
+discard_from_cache(ldap_cache_t *cache, dns_name_t *name)
+{
+ isc_result_t result;
+
+ REQUIRE(cache != NULL);
+ REQUIRE(name != NULL);
+
+ if (cache->rbt == NULL) {
+ result = ISC_R_SUCCESS;
+ } else {
+ LOCK(&cache->mutex);
+ result = dns_rbt_deletename(cache->rbt, name, ISC_FALSE);
+ UNLOCK(&cache->mutex);
+ }
+
+ if (result == ISC_R_NOTFOUND)
+ result = ISC_R_SUCCESS;
+
+ return result;
+}