summaryrefslogtreecommitdiff
path: root/plugin/autoserial.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugin/autoserial.c')
-rw-r--r--plugin/autoserial.c573
1 files changed, 573 insertions, 0 deletions
diff --git a/plugin/autoserial.c b/plugin/autoserial.c
new file mode 100644
index 0000000..d10f8a5
--- /dev/null
+++ b/plugin/autoserial.c
@@ -0,0 +1,573 @@
+
+#include "config.h"
+
+#include "plugin.h"
+
+#include "common/soa.h"
+#include "common/strset.h"
+
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+/*----------------------------------------------------------------------------------
+ * DECLARATIONS
+ */
+
+/* TODO: Fill these in from settings */
+static const char *dnsserial_soa_attribute = "sOARecord";
+static const char *dnsserial_soa_base = "";
+
+static Slapi_Mutex *dnsserial_mutex = NULL;
+static strset *dnsserial_soa_cache = NULL;
+
+/* ---------------------------------------------------------------------------------
+ * MEMORY ALLOCATION
+ */
+
+void*
+strset_malloc (size_t length)
+{
+ return slapi_ch_malloc (length);
+}
+
+void
+strset_free (void *ptr)
+{
+ return slapi_ch_free (&ptr);
+}
+
+/* ---------------------------------------------------------------------------------
+ * LDAP OPERATIONS
+ */
+
+static int
+search_for_dns (const char *base, const char *filter, char ***results)
+{
+ Slapi_Entry **entries, **e;
+ Slapi_PBlock *pb;
+ LDAPControl *ctrl;
+ char **dns, **d;
+ char *attr, *dn;
+ int rc, num, code;
+
+ assert (results);
+ assert (filter && filter[0]);
+ assert (base && base[0]);
+
+ ctrl = NULL; /* No controls */
+ attr = NULL; /* No attributes */
+
+ trace ("performing internal search");
+
+ /* Do the actual search */
+ pb = slapi_search_internal ((char*)base, LDAP_SCOPE_SUBTREE, (char*)filter, &ctrl, &attr, 1);
+ return_val_if_fail (pb, -1);
+
+ /* Was it successful? */
+ code = -1;
+ rc = slapi_pblock_get (pb, SLAPI_PLUGIN_INTOP_RESULT, &code);
+ return_val_if_fail (rc >= 0, -1);
+ if (code != LDAP_SUCCESS) {
+ log_plugin ("error performing search on %s for %s (code %d)", base, filter, code);
+ slapi_pblock_destroy (pb);
+ trace ("failure");
+ return -1;
+ }
+
+ /* Dig out all the entries */
+ entries = NULL;
+ rc = slapi_pblock_get (pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ return_val_if_fail (rc >= 0, -1);
+
+ /* How many entries? */
+ num = 0;
+ for (e = entries; entries && *e; ++e)
+ ++num;
+
+ /* Allocate memory and copy over all dns found */
+ d = dns = (char**)slapi_ch_calloc (num + 1, sizeof (char*));
+ for (e = entries; entries && *e; ++e) {
+ dn = slapi_entry_get_dn (*e);
+ return_val_if_fail (dn, -1);
+
+ dn = slapi_ch_strdup (dn);
+ dn = slapi_dn_normalize_case (dn);
+ *(d++) = dn;
+ }
+
+ slapi_pblock_destroy (pb);
+ *results = dns;
+
+ trace ("success");
+ return 0;
+}
+
+static int
+populate_soa_cache (void)
+{
+ char **dns, **d;
+ char *filter;
+ int rc, len, have;
+
+ assert (dnsserial_mutex);
+
+ have = 0;
+ slapi_lock_mutex (dnsserial_mutex);
+
+ /* Already populated? */
+ have = dnsserial_soa_cache ? 1 : 0;
+
+ slapi_unlock_mutex (dnsserial_mutex);
+
+ if (have)
+ return 1;
+
+ /* Build up the filter */
+ len = strlen (dnsserial_soa_attribute) +
+ strlen ("(%s=*)");
+ filter = slapi_ch_malloc (len);
+ strcpy (filter, "(");
+ strcat (filter, dnsserial_soa_attribute);
+ strcat (filter, "=*)");
+
+ trace (filter);
+
+ rc = search_for_dns (dnsserial_soa_base, filter, &dns);
+ slapi_ch_free_string (&filter);
+
+ if (rc < 0)
+ return rc;
+
+ /* Normalize all the DNs */
+ for (d = dns; *d; ++d)
+ slapi_dn_normalize_case (*d);
+
+ /* Now we're ready to put them in the cache */
+ assert (dnsserial_mutex);
+ slapi_lock_mutex (dnsserial_mutex);
+
+ /* Already populated? */
+ if (!dnsserial_soa_cache) {
+ dnsserial_soa_cache = strset_create ();
+ for (d = dns; *d; ++d)
+ strset_add (dnsserial_soa_cache, *d);
+ }
+
+ slapi_unlock_mutex (dnsserial_mutex);
+
+ slapi_ch_array_free (dns);
+ return 1;
+}
+
+static int
+present_in_soa_cache (const char *dn)
+{
+ int present;
+
+ assert (dn && dn[0]);
+ assert (dnsserial_mutex);
+
+ if (populate_soa_cache () < 0)
+ return 0;
+
+ slapi_lock_mutex (dnsserial_mutex);
+
+ present = strset_has (dnsserial_soa_cache, dn);
+
+ slapi_unlock_mutex (dnsserial_mutex);
+
+ return present;
+}
+
+static char*
+ancestor_in_soa_cache (const char *dn)
+{
+ Slapi_DN *sdn = NULL;
+ Slapi_DN *parent = NULL;
+ char *ancestor;
+
+ assert (dn && dn[0]);
+ assert (dnsserial_mutex);
+
+ if (populate_soa_cache () < 0)
+ return 0;
+
+ sdn = slapi_sdn_new_dn_byval (dn);
+ return_val_if_fail (sdn, 0);
+
+ ancestor = NULL;
+ slapi_lock_mutex (dnsserial_mutex);
+
+ for (;;) {
+
+ /* Get the parent of the dn */
+ assert (sdn);
+ assert (!parent);
+ parent = slapi_sdn_new ();
+ slapi_sdn_get_parent (sdn, parent);
+
+ /* Reached the root? */
+ if (slapi_sdn_isempty (parent))
+ break;
+
+ /* Reached the top of our SOA block? */
+ dn = slapi_sdn_get_dn (parent);
+ if (strcmp (dn, dnsserial_soa_base) == 0)
+ break;
+
+ /* Found this DN? */
+ if (strset_has (dnsserial_soa_cache, dn)) {
+ ancestor = slapi_ch_strdup (dn);
+ break;
+ }
+
+ slapi_sdn_free (&sdn);
+ sdn = parent;
+ parent = NULL;
+ }
+
+
+ slapi_unlock_mutex (dnsserial_mutex);
+
+ if (sdn)
+ slapi_sdn_free (&sdn);
+ if (parent)
+ slapi_sdn_free (&parent);
+
+ return ancestor;
+}
+
+static int
+add_to_soa_cache (const char *dn)
+{
+ assert (dn && dn[0]);
+ assert (dnsserial_mutex);
+
+ if (populate_soa_cache () < 0)
+ return -1;
+
+ slapi_lock_mutex (dnsserial_mutex);
+
+ strset_add (dnsserial_soa_cache, dn);
+
+ slapi_unlock_mutex (dnsserial_mutex);
+
+ return 0;
+}
+
+static int
+remove_from_soa_cache (const char *dn)
+{
+ assert (dn && dn[0]);
+
+ if (populate_soa_cache () < 0)
+ return -1;
+
+ slapi_lock_mutex (dnsserial_mutex);
+
+ strset_remove (dnsserial_soa_cache, dn);
+
+ slapi_unlock_mutex (dnsserial_mutex);
+
+ return 0;
+}
+
+static char*
+load_soa_attribute (const char *dn)
+{
+ Slapi_Entry **entries;
+ struct berval **values;
+ Slapi_PBlock *pb;
+ Slapi_Attr *attr;
+ LDAPControl *ctrl;
+ char *attrs[2];
+ char *soa;
+ int rc, code;
+
+ trace (dn);
+ assert (dn && dn[0]);
+ assert (dnsserial_soa_attribute);
+
+ ctrl = NULL; /* No controls */
+ attrs[0] = (char*)dnsserial_soa_attribute;
+ attrs[1] = NULL;
+
+ trace ("performing internal search");
+
+ /* Do the actual search */
+ pb = slapi_search_internal ((char*)dn, LDAP_SCOPE_BASE, "(objectClass=*)", &ctrl, attrs, 0);
+ return_val_if_fail (pb, NULL);
+
+ /* Was it successful? */
+ code = -1;
+ rc = slapi_pblock_get (pb, SLAPI_PLUGIN_INTOP_RESULT, &code);
+ return_val_if_fail (rc >= 0, NULL);
+ if (code != LDAP_SUCCESS) {
+ log_plugin ("error loading attribute %s from %s (code %d)", dnsserial_soa_attribute, dn, code);
+ slapi_pblock_destroy (pb);
+ trace ("failure");
+ return NULL;
+ }
+
+ /* Dig out all the entries */
+ entries = NULL;
+ slapi_pblock_get (pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ return_val_if_fail (entries, NULL);
+
+ soa = NULL;
+ if (entries[0]) {
+ if (slapi_entry_attr_find (entries[0], (char*)dnsserial_soa_attribute, &attr) >= 0 && attr &&
+ slapi_attr_get_values (attr, &values) >= 0 && values && values[0]) {
+
+ /* Convert the berval into a string */
+ soa = slapi_ch_malloc (values[0]->bv_len + 1);
+ if (values[0]->bv_len)
+ memcpy (soa, values[0]->bv_val, values[0]->bv_len);
+ soa[values[0]->bv_len] = 0;
+ }
+ }
+
+ slapi_pblock_destroy (pb);
+ trace ("success");
+
+ return soa;
+}
+
+static int
+change_soa_attribute (const char *dn, const char *soa_old, const char *soa_new)
+{
+ Slapi_PBlock *pb;
+ LDAPMod *mods[3];
+ char *values_add[2];
+ char *values_remove[2];
+ LDAPMod mod_add;
+ LDAPMod mod_remove;
+ int rc, code;
+
+ trace (dn);
+
+ assert (dn && dn[0]);
+ assert (soa_old);
+ assert (soa_new);
+
+ /* Setup the mods to change */
+ values_add[0] = (char*)soa_new;
+ values_add[1] = NULL;
+ mod_add.mod_op = LDAP_MOD_ADD;
+ mod_add.mod_type = (char*)dnsserial_soa_attribute;
+ mod_add.mod_values = values_add;
+
+ values_remove[0] = (char*)soa_old;
+ values_remove[1] = NULL;
+ mod_remove.mod_op = LDAP_MOD_DELETE;
+ mod_remove.mod_type = (char*)dnsserial_soa_attribute;
+ mod_remove.mod_values = values_remove;
+
+ mods[0] = &mod_remove;
+ mods[1] = &mod_add;
+ mods[2] = NULL;
+
+ trace ("performing modify internal");
+
+ pb = slapi_modify_internal ((char*)dn, mods, NULL, 1);
+ return_val_if_fail (pb, -1);
+
+ code = -1;
+ rc = slapi_pblock_get (pb, SLAPI_PLUGIN_INTOP_RESULT, &code);
+ return_val_if_fail (pb, -1);
+ slapi_pblock_destroy (pb);
+
+ if (code != LDAP_SUCCESS && code != LDAP_TYPE_OR_VALUE_EXISTS && code != LDAP_NO_SUCH_ATTRIBUTE) {
+ log_plugin ("couldn't replace %s on %s with '%s' (code %d)",
+ dnsserial_soa_attribute, dn, soa_new, code);
+ trace ("failure");
+ return -1;
+ }
+
+ trace ("success");
+ return 0;
+}
+
+static int
+increment_soa_dn (const char *dn)
+{
+ char *soa_new;
+ char *soa_old;
+ int len, ret = -1;
+
+ assert (dn && dn[0]);
+
+ soa_old = load_soa_attribute (dn);
+ if (!soa_old) {
+ log_plugin ("couldn't increment SOA record at %s: not found", dn);
+ return -1;
+ }
+
+ len = strlen (soa_old) + 32;
+ soa_new = slapi_ch_malloc (len);
+ if (soa_serial_increment (soa_old, soa_new, len) < 0) {
+ if (errno == EINVAL)
+ log_plugin ("invalid SOA record at %s: %s", dn, soa_old);
+ else
+ log_plugin ("couldn't increment SOA record at %s: %s", dn, strerror (errno));
+ ret = -1;
+ } else {
+ ret = change_soa_attribute (dn, soa_old, soa_new);
+ }
+
+ slapi_ch_free_string (&soa_new);
+ slapi_ch_free_string (&soa_old);
+
+ return ret;
+}
+
+void
+autoserial_post_delete (const char *dn)
+{
+ char *soa_dn;
+
+ return_if_fail (dn && dn[0]);
+
+ /* A DN that is an SOA? */
+ if (present_in_soa_cache (dn)) {
+ remove_from_soa_cache (dn);
+
+ /* A DN underneath an SOA? */
+ } else {
+ soa_dn = ancestor_in_soa_cache (dn);
+ if (soa_dn) {
+ increment_soa_dn (soa_dn);
+ slapi_ch_free_string (&soa_dn);
+ }
+ }
+}
+
+void
+autoserial_post_modify (const char *dn, LDAPMod **mods)
+{
+ char *soa, *soa_dn;
+ int present;
+
+ return_if_fail (dn && dn[0]);
+
+ /* Add or remove from cache as appropriate */
+ soa = load_soa_attribute(dn);
+ if (soa) {
+ add_to_soa_cache (dn);
+ slapi_ch_free_string (&soa);
+ present = 1;
+ } else {
+ remove_from_soa_cache (dn);
+ present = 0;
+ }
+
+ /* A DN underneath an SOA? */
+ if (!present) {
+ soa_dn = ancestor_in_soa_cache (dn);
+ if (soa_dn) {
+ increment_soa_dn (soa_dn);
+ slapi_ch_free_string (&soa_dn);
+ }
+ }
+}
+
+void
+autoserial_post_modrdn (const char *odn, const char *ndn)
+{
+ char *soa_dn;
+
+ return_if_fail (odn && odn[0]);
+ return_if_fail (ndn && ndn[0]);
+
+ /* A DN that is an SOA? */
+ if (present_in_soa_cache (odn)) {
+
+ remove_from_soa_cache (odn);
+ add_to_soa_cache (ndn);
+
+ /* A DN underneath an SOA? */
+ } else {
+ soa_dn = ancestor_in_soa_cache (ndn);
+ if (soa_dn) {
+ increment_soa_dn (soa_dn);
+ slapi_ch_free_string (&soa_dn);
+ }
+ }
+}
+
+void
+autoserial_post_add (const char *dn)
+{
+ char *soa, *soa_dn;
+
+ return_if_fail (dn && dn[0]);
+
+ /* A DN that is an SOA? */
+ soa = load_soa_attribute (dn);
+ if (soa) {
+ add_to_soa_cache (dn);
+ slapi_ch_free_string (&soa);
+
+ /* A DN underneath an SOA? */
+ } else {
+ soa_dn = ancestor_in_soa_cache (dn);
+ if (soa_dn) {
+ increment_soa_dn (soa_dn);
+ slapi_ch_free_string (&soa_dn);
+ }
+ }
+}
+
+int
+autoserial_config (const char *name, const char *value)
+{
+ return_val_if_fail (name, 0);
+
+ if (strcmp ("soa-attribute", name) == 0 && value) {
+ dnsserial_soa_attribute = value;
+ return 1;
+
+
+ } else if (strcmp ("base-dn", name) == 0 && value) {
+ dnsserial_soa_base = value;
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+autoserial_destroy (void)
+{
+ assert (dnsserial_mutex);
+ slapi_lock_mutex (dnsserial_mutex);
+
+ if (dnsserial_soa_cache)
+ strset_destroy (dnsserial_soa_cache);
+ dnsserial_soa_cache = NULL;
+
+ slapi_unlock_mutex (dnsserial_mutex);
+
+ slapi_destroy_mutex (dnsserial_mutex);
+ dnsserial_mutex = NULL;
+}
+
+int
+autoserial_init (void)
+{
+ if (!dnsserial_soa_base || !dnsserial_soa_base[0]) {
+ log_plugin ("'base-dn' argument not specified");
+ return -1;
+ }
+
+ assert (!dnsserial_mutex);
+ dnsserial_mutex = slapi_new_mutex ();
+ return_val_if_fail (dnsserial_mutex, -1);
+
+ return 0;
+}