diff options
Diffstat (limited to 'plugin/autoserial.c')
-rw-r--r-- | plugin/autoserial.c | 573 |
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; +} |